Elixir代码最终编译成为erlang代码,这个过程是怎样的?本文通过一个小测试做下探索.
 
 
 
 
编译一旦完成,你就看到了真相
 
Elixir代码组织方式一方面和Erlang一样才用非常扁平的代码模块结构,另一方面Elixir同时支持嵌套.Elixir比较方便的一点是可以在Elixir Shell中完成对模块的定义.看下面的方式:
 
iex> defmodule Math do
...> def sum(a, b) do
...> a + b
...> end
...> end iex> Math.sum(1, 2)
3
下面我们把代码放在m.ex模块中,模块的名字和代码文件的名字是可以不一样的,在编译之后文件夹中新增了一个Elixir.Math.beam的文件.换句话说,elixirc已经把m.ex文件编译成Elixir.Math.beam,按照Erlang对模块名称和文件名一致性的要求,我们可以在Erlang的Shell中验证一下:
 
 
[root@nimbus elixir]# elixirc m.ex
[root@nimbus elixir]# ls
Elixir.Math.beam m.ex [root@nimbus elixir]# erl
Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V5.10.2 (abort with ^G)
1> 'Elixir.Math':sum(12,23).
35
2>
   是不是和我们预期的一样?之前提到过多次从beam中还原源代码的方法,现在我们动手看下这个Elixir.Math.beam的Erlang代码是怎样的,输出我做了一下排版:
 
> {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks("Elixir.Math",[abstract_code]).
{ok,{'Elixir.Math',
[{abstract_code,
{raw_abstract_v1,
[{attribute,,compile, {no_auto_import,[{bitsize,},{apply,},
{spawn,}, {spawn_link,}, {spawn_monitor,}, {spawn_opt,},
{spawn_opt,}, {spawn,},
{spawn_link,}, {spawn_opt,},
{spawn_opt,}, {nodes,...},
{...}|...]}},
{attribute,,file,{"/data2/elixir/m.ex",}},
{attribute,,module,'Elixir.Math'},
{attribute,,export,[{'__info__',},{sum,}]},
{function,,'__info__',,
[{clause,,
[{atom,,functions}],
[],
[{cons,,{...},...}]},
{clause,,[{atom,,macros}],[],[{nil,}]},
{clause,,[{atom,,docs}],[],[{cons,...}]},
{clause,,[{atom,,...}],[],[{...}]},
{clause,,[{atom,...}],[],[...]},
{clause,,[{...}],[],...}]},
{function,,sum,,
[{clause,,
[{var,,a},{var,,b}],
[],
[{op,,...}]}]}]}}]}}
> io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).
-compile({no_auto_import,
[{bitsize, }, {apply, }, {spawn, }, {spawn_link, },
{spawn_monitor, }, {spawn_opt, }, {spawn_opt, },
{spawn, }, {spawn_link, }, {spawn_opt, },
{spawn_opt, }, {nodes, }, {disconnect_node, },
{integer_to_list, }, {integer_to_binary, }, {max, },
{min, }, {port_control, }, {port_connect, },
{port_command, }, {port_command, }, {port_close, },
{spawn_monitor, }, {spawn, }, {load_module, },
{spawn_link, }, {binary_to_float, },
{float_to_binary, }, {float_to_binary, },
{list_to_integer, }, {integer_to_binary, },
{binary_to_integer, }, {binary_to_integer, },
{check_old_code, }, {binary_part, }, {binary_part, },
{binary_to_term, }, {binary_to_existing_atom, },
{binary_to_atom, }, {atom_to_binary, },
{bitstring_to_list, }, {list_to_bitstring, },
{bit_size, }, {byte_size, }, {tuple_size, },
{is_bitstring, }, {list_to_existing_atom, },
{iolist_to_binary, }, {iolist_size, },
{is_boolean, }, {is_record, }, {is_record, },
{is_function, }, {is_function, }, {is_binary, },
{is_reference, }, {is_port, }, {is_pid, },
{is_number, }, {is_integer, }, {is_float, },
{is_tuple, }, {is_list, }, {is_atom, }, {error, },
{error, }, {is_process_alive, }, {demonitor, },
{demonitor, }, {monitor, }, {whereis, },
{unregister, }, {unlink, }, {tuple_to_list, },
{trunc, }, {tl, }, {time, }, {throw, },
{term_to_binary, }, {term_to_binary, },
{statistics, }, {split_binary, }, {spawn_link, },
{spawn, }, {size, }, {setelement, }, {self, },
{round, }, {registered, }, {register, }, {put, },
{purge_module, }, {processes, }, {process_info, },
{process_info, }, {process_flag, }, {process_flag, },
{pre_loaded, }, {pid_to_list, }, {open_port, },
{now, }, {nodes, }, {node, }, {node, },
{monitor_node, }, {module_loaded, }, {make_ref, },
{list_to_tuple, }, {list_to_pid, },
{list_to_integer, }, {list_to_float, },
{list_to_binary, }, {list_to_atom, }, {link, },
{length, }, {is_alive, }, {integer_to_list, },
{hd, }, {halt, }, {halt, }, {halt, },
{group_leader, }, {group_leader, }, {get_keys, },
{get, }, {get, }, {garbage_collect, },
{garbage_collect, }, {float_to_list, },
{float_to_list, }, {float, }, {exit, }, {exit, },
{erase, }, {erase, }, {element, },
{delete_module, }, {date, }, {check_process_code, },
{binary_to_term, }, {binary_to_list, },
{binary_to_list, }, {atom_to_list, }, {apply, },
{abs, }]}). -file("/data2/elixir/m.ex", ). -module('Elixir.Math'). -export(['__info__'/, sum/]). '__info__'(functions) -> [{sum, }];
'__info__'(macros) -> [];
'__info__'(docs) ->
[{{sum, }, , def,
[{a, [{line, }], nil}, {b, [{line, }], nil}], nil}];
'__info__'(moduledoc) -> {, nil};
'__info__'(module) -> 'Elixir.Math';
'__info__'(atom) -> module_info(atom). sum(a, b) -> a + b.
ok
>

如何编译的?

 
 
  下面我们探究一下Elixir编译的过程,切入点当然是elixirc,打开这个脚本:
 
 
可以看到完成了一些环境变量解析之后,最终是调用了elixir
exec "$SCRIPT_PATH"/elixir +compile "$@"
OK,我们继续跟进elixir,经过一番参数检查,变量解析后,最后执行的命令类似下面:
  
 erl -pa "$SCRIPT_PATH"/../lib/*/ebin -noshell  -s elixir start_cli -extra +compile
简单回顾一下erlang 运行时环境启动的参数,erl的参数分三种:加号+后面跟的是 emulator flags,单连字符"-"后面跟的是flags,init进程会完成这些参数的解析; -extra 后面跟的内容都会被当做是plain arguments. http://erlang.org/doc/man/erl.html
  
% erl +W w -sname arnie +R  -s my_init -extra +bertie
(arnie@host)> init:get_argument(sname).
{ok,[["arnie"]]}
(arnie@host)> init:get_plain_arguments().
["+bertie"]

Here +W w and +R 9 are emulator flags. -s my_init is an init flag, interpreted by init. -sname arnie is a user flag, stored by init. It is read by Kernel and will cause the Erlang runtime system to become distributed. Finally, everything after -extra (that is, +bertie) is considered as plain arguments.

% erl -myflag
> init:get_argument(myflag).
{ok,[[""]]}
> init:get_plain_arguments().
[]

Here the user flag -myflag 1 is passed to and stored by the init process. It is a user defined flag, presumably used by some user defined application.

 
 
 书归正传,elixir代码里面给我们后续跟进的线索"-s elixir start_cli",废话少说,打开文件:
 
 
%% Boot and process given options. Invoked by Elixir's script.
start_cli() ->
application:start(?MODULE), 'Elixir.Kernel.CLI':main(init:get_plain_arguments()).
 
ok,下面我们手工完成m.ex文件的编译过程(为了方便执行你可以去/elixir/lib/elixir/ebin文件夹),我们分两步1.启动elixir 2.调用编译函数 'Elixir.Kernel.CLI':main(["+compile","m.ex"]).之所以要启动elixir,是为了完成类似code_server的职责.
 
[root@nimbus ebin]# erl
Erlang R16B01 (erts-5.10.) [source] [-bit] [smp::] [async-threads:] [hipe] [kernel-poll:false] Eshell V5.10.2 (abort with ^G)
> application:start(elixir).
ok > 'Elixir.Kernel.CLI':main(["+compile","m.ex"]).
检查文件夹中的文件,是不是已经编译好了.
 
{ok,"今天就到这里."}
 
 
如果你要继续跟进代码,你可以看到:
  @doc """
This is the API invoked by Elixir boot process.
"""
def main(argv) do
argv = lc arg inlist argv, do: String.from_char_list!(arg) { config, argv } = process_argv(argv, Kernel.CLI.Config.new)
System.argv(argv) run fn ->
command_results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
command_errors = lc { :error, msg } inlist command_results, do: msg
errors = Enum.reverse(config.errors) ++ command_errors if errors != [] do
Enum.each(errors, &IO.puts(:stderr, &1))
System.halt(1)
end
end, config.halt
end

 

 defp process_argv(["+compile"|t], config) do
process_compiler t, config
end

  

 
 
 
最后,于"金蝉脱壳"上映之际小图一张  
 

[Erlang 0109] From Elixir to Erlang Code的更多相关文章

  1. elixir 调用erlang 代码

    备注:    项目比较简单,主要是elixir 混合erlang 代码,elixir 调用erlang 模块方法   1. 初始化项目   mix new erlangelixirdemo 项目结构如 ...

  2. [Erlang 0116] 当我们谈论Erlang Maps时,我们谈论什么 Part 1

         Erlang 增加 Maps数据类型并不是很突然,因为这个提议已经进行了2~3年之久,只不过Joe Armstrong老爷子最近一篇文章Big changes to Erlang掀起不小了风 ...

  3. [Erlang 0117] 当我们谈论Erlang Maps时,我们谈论什么 Part 2

    声明:本文讨论的Erlang Maps是基于17.0-rc2,时间2014-3-4.后续Maps可能会出现语法或函数API上的有所调整,特此说明. 前情提要: [Erlang 0116] 当我们谈论E ...

  4. Erlang基础 -- 介绍 -- 历史及Erlang并发

    前言 最近在总结一些Erlang编程语言的基础知识,拟系统的介绍Erlang编程语言,从基础到进阶,然后再做Erlang编程语言有意思的库的分析. 其实,还是希望越来越多的人关注Erlang,使用Er ...

  5. [Erlang 0125] Know a little Erlang opcode

    Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的 ...

  6. [Erlang 0128] Term sharing in Erlang/OTP 下篇

    继续昨天的话题,昨天提到io:format对数据共享的间接影响,如果是下面两种情况恐怕更容易成为"坑", 呃,恰好我都遇到过; 如果是测试代码是下面这样,得到的结果会是怎样?猜! ...

  7. [Erlang 0127] Term sharing in Erlang/OTP 上篇

    之前,在 [Erlang 0126] 我们读过的Erlang论文 提到过下面这篇论文: On Preserving Term Sharing in the Erlang Virtual Machine ...

  8. [Erlang 0121] 当我们谈论Erlang Maps时,我们谈论什么 Part 3

    Erlang/OTP 17.0 has been released  http://www.erlang.org/download/otp_src_17.0.readme     Erlang/OTP ...

  9. [Erlang 0113] Elixir 编译流程梳理

    注意:目前Elixir版本还不稳定,代码调整较大,本文随时失效      之前简单演示过如何从elixir ex代码生成并运行Erlang代码,下面仔细梳理一遍elixir文件的编译过程,书接上文,从 ...

随机推荐

  1. ABP框架 - 时间

    文档目录 本节内容: 简介 时钟 客户端 时区 客户端 绑定器与转换器 简介 虽然有些应用目标市场只是在一个时区,有些应用目标市场是许多不同时区,为满足这种需求并集中化日期操作,ABP为日期操作提供公 ...

  2. 初学者--bootstrap(六)组件中的字体图标----在路上(9)

    组件---字体图标 无数可复用的组件,包括字体图标.下拉菜单.导航.警告框.弹出框等更多功能. 1.如何使用:        出于性能的考虑,所有图标都需要一个基类和对应每个图标的类.把下面的代码放在 ...

  3. 扎克伯格开发的家用AI: Jarvis

    扎克伯格本周二在facebook发布了一篇文章,介绍自己利用个人时间开发的一套在自己家里使用的AI系统,并将它命名为Jarvis,对!就是电影钢铁侠里的AI助手Jarvis. 文章并没有讲细节的技术c ...

  4. 设计模式之迪米特原则(LOD)(最少知识原则)

    来源:迪米特法则(LoD)最初是用来作为面向对象的系统设计风格的一种法则,是很多著名系统,如火星登陆软件系统.木星的欧罗巴卫星轨道飞船的软件系统的指导设计原则. 迪米特法则(LoD)又可分为两种:狭义 ...

  5. CSS3与页面布局学习总结(四)——页面布局大全

    一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...

  6. 【NLP】基于统计学习方法角度谈谈CRF(四)

    基于统计学习方法角度谈谈CRF 作者:白宁超 2016年8月2日13:59:46 [摘要]:条件随机场用于序列标注,数据分割等自然语言处理中,表现出很好的效果.在中文分词.中文人名识别和歧义消解等任务 ...

  7. 2.C#WinForm基础Email分析器

    功能:输入Email地址,输出用户名和域名 string[] String.split(params char[] separator)(+5重载)) 返回的字符串数组包含此实例的字符串(由指定Uni ...

  8. Android测试提升效率批处理脚本(三)

    前言: 前面放出过几次批处理,这次只放一个环境检查的被管理员给打回来了,不得不再找找几个有含金量的放出来,请看正文~~~ 目录 1.Android环境检查 2.Android内存监控 3.模拟蓝牙手柄 ...

  9. FFmpeg学习4:音频格式转换

    前段时间,在学习试用FFmpeg播放音频的时候总是有杂音,网上的很多教程是基于之前版本的FFmpeg的,而新的FFmepg3中audio增加了平面(planar)格式,而SDL播放音频是不支持平面格式 ...

  10. 一步一步开发Game服务器(四)地图线程

    时隔这么久 才再一次的回归正题继续讲解游戏服务器开发. 开始讲解前有一个问题需要修正.之前讲的线程和定时器线程的时候是分开的. 但是真正地图线程与之前的线程模型是有区别的. 为什么会有区别呢?一个地图 ...