注意:目前Elixir版本还不稳定,代码调整较大,本文随时失效
1
2
3
4
5
6
|
-module(elixir).
......
start_cli() ->
application:start(?MODULE),
%% start_cli() --> [
"+compile"
,
"m.ex"
]
'Elixir.Kernel.CLI'
:main(init:get_plain_arguments()).
|
1
2
3
4
5
6
7
8
9
10
11
|
defmodule Kernel.CLI
do
......
if
files != []
do
wrapper fn ->
Code.compiler_options(config.compiler_options)
Kernel.ParallelCompiler.files_to_path(files, config.output,
each_file: fn file ->
if
config.verbose_compile
do
IO.puts
"Compiled #{file}"
end end)
end
else
{ :error,
"--compile : No files matched patterns #{Enum.join(patterns, "
,
")}"
}
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
defmodule Kernel.ParallelCompiler
do
.....
try
do
if
output
do
:elixir_compiler.file_to_path(h, output)
else
:elixir_compiler.file(h)
end
parent <- { :compiled, self(), h }
catch
kind, reason ->
parent <- { :failure, self(), kind, reason, System.stacktrace }
end
|
1
2
3
4
5
6
7
8
9
10
11
12
|
-module(elixir_compiler).
file(Relative) when is_binary(Relative) ->
File = filename:absname(Relative),
{ ok, Bin } = file:read_file(File),
string
(elixir_utils:characters_to_list(Bin), File).
string
(Contents, File) when is_list(Contents), is_binary(File) ->
Forms = elixir_translator:
'forms!'
(Contents, 1, File, []),
quoted(Forms, File).
|
1
2
3
4
5
6
7
8
9
10
11
|
[{defmodule,
[{line,1}],
[{
'__aliases__'
,[{line,1}],[
'Math'
]},
[{
do
,
{def,[{line,2}],[{sum,[{line,2}],[{a,[{line,2}],nil},{b,[{line,2}],nil}]},
[{
do
,{
'__block__'
,[],[
{
'='
,[{line,3}],[{a,[{line,3}],nil},123]},
{
'='
,[{line,4}],[{a,[{line,4}],nil},2365]},
{
'+'
,[{line,5}],[
{a,[{line,5}],nil},{b,[{line,5}],nil}]}
]}}]]}}]]}]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
-record(elixir_scope, {
context=nil, %% can be assign, guards or nil
extra=nil, %% extra information about the context, like fn_match
for
fns
noname=
false
, %% when
true
, don't add
new
names (used
by
try
)
super=
false
, %% when
true
, it means super was invoked
caller=
false
, %% when
true
, it means caller was invoked
module=nil, %% the current module
function=nil, %% the current function
vars=[], %% a dict of defined variables and their alias
backup_vars=nil, %% a copy of vars to be used
on
^
var
temp_vars=nil, %% a
set
of all variables defined
in
a particular assign
clause_vars=nil, %% a dict of all variables defined
in
a particular clause
extra_guards=nil, %% extra guards
from
args expansion
counter=[], %% a counter
for
the variables defined
local=nil, %% the scope to evaluate local functions against
context_modules=[], %% modules defined
in
the current context
macro_aliases=[], %% keep aliases defined inside a macro
macro_counter=0, %% macros expansions counter
lexical_tracker=nil, %% holds the lexical tracker pid
aliases, %% an orddict with aliases
by
new
-> old names
file, %% the current scope filename
requires, %% a
set
with modules required
macros, %% a list with macros imported
from
module
functions %% a list with functions imported
from
module
}).
|
quoted方法最近的变化是使用elixir_lexical:run包装了一下,之前的版本简单直接,可以先看一下:
1
2
3
4
5
6
7
8
9
10
|
quoted(Forms, File) when is_binary(File) ->
Previous =
get
(elixir_compiled),
% M:elixir_compiler Previous undefined
try
put(elixir_compiled, []),
eval_forms(Forms, 1, [], elixir:scope_for_eval([{file,File}])),
lists:reverse(
get
(elixir_compiled))
after
put(elixir_compiled, Previous)
end.
|
现在quoted是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
quoted(Forms, File) when is_binary(File) ->
Previous =
get
(elixir_compiled),
try
put(elixir_compiled, []),
elixir_lexical:run(File, fun
(Pid) ->
Scope = elixir:scope_for_eval([{file,File}]),
eval_forms(Forms, 1, [], Scope#elixir_scope{lexical_tracker=Pid})
end),
lists:reverse(
get
(elixir_compiled))
after
put(elixir_compiled, Previous)
end.
|
quoted方法里面我们需要重点关注的是eval_forms方法,在这个方法里面完成了Elixir AST到Erlang AST转换,Elixir表达式通过 elixir_translator:translate被翻译成对应的Erlang Abstract Format.之后eval_mod(Fun, Exprs, Line, File, Module, Vars)完成对表达式和代码其它部分(比如attribute,等等)进行组合.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
eval_forms(Forms, Line, Vars, S) ->
{ Module, I } = retrieve_module_name(),
{ Exprs, FS } = elixir_translator:translate(Forms, S),
Fun = eval_fun(S#elixir_scope.module),
Form = eval_mod(Fun, Exprs, Line, S#elixir_scope.file, Module, Vars),
Args = list_to_tuple([V || { _, V } <- Vars]),
%% Pass { native,
false
} to speed up bootstrap
%% process when native
is
set
to
true
{ module(Form, S#elixir_scope.file, [{native,
false
}],
true
,
fun(_, Binary) ->
Res = Module:Fun(Args),
code:delete(Module),
%% If we have labeled locals, anonymous functions
%% were created and therefore we cannot ditch the
%% module
case
beam_lib:chunks(Binary, [labeled_locals]) of
{ ok, { _, [{ labeled_locals, []}] } } ->
code:purge(Module),
return_module_name(I);
_ ->
ok
end,
Res
end), FS }.
|
最后完成编译和加载的重头戏就在module(Forms, File, Opts, Callback)方法了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
%% Compile the module
by
forms based
on
the scope information
%% executes the callback
in
case
of success. This automatically
%% handles errors and warnings. Used
by
this
module and elixir_module.
module(Forms, File, Opts, Callback) ->
DebugInfo = (get_opt(debug_info) ==
true
) orelse lists:member(debug_info, Opts),
Final =
if
DebugInfo -> [debug_info];
true
-> []
end,
module(Forms, File, Final,
false
, Callback).
module(Forms, File, RawOptions, Bootstrap, Callback) when
is_binary(File), is_list(Forms), is_list(RawOptions), is_boolean(Bootstrap), is_function(Callback) ->
{ Options, SkipNative } = compile_opts(Forms, RawOptions),
Listname = elixir_utils:characters_to_list(File),
case
compile:noenv_forms([no_auto_import()|Forms], [
return
,{source,Listname}|Options]) of
{ok, ModuleName, Binary, RawWarnings} ->
Warnings =
case
SkipNative of
true
-> [{?MODULE,[{0,?MODULE,{skip_native,ModuleName}}]}|RawWarnings];
false
-> RawWarnings
end,
format_warnings(Bootstrap, Warnings),
%%%% ModuleName :
'Elixir.Math'
ListName:
"/data2/elixir/m.ex"
code:load_binary(ModuleName, Listname, Binary),
Callback(ModuleName, Binary);
{error, Errors, Warnings} ->
format_warnings(Bootstrap, Warnings),
format_errors(Errors)
end.
|
到这里编译的流程已经走完,附上几个可能会用到的资料,首先是elixir_parser:parse(Tokens)的代码,你可能会奇怪这个模块的代码在哪里?这个是通过elixir_parser.yrl编译自动生成的模块,你可以使用下面的方法拿到它的代码:
1
2
3
4
5
6
7
8
9
10
11
|
Eshell V5.10.2 (abort with ^G)
1> {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(
"elixir_parser"
,[abstract_code]).
{ok,{elixir_parser,
[{abstract_code,
........
{...}|...]}}]}}
2> Dump= fun(Content)-> file:write_file(
"/data/dump.data"
, io_lib:fwrite(
"~ts.\n"
, [Content])) end.
#Fun<erl_eval.6.80484245>
3> Dump(erl_prettypr:format(erl_syntax:form_list(AC))).
ok
4>
|
- elixir_aliases的设计
- elixir_scope 的设计
- elixir macro 相关的几个话题:hygiene unquote_splicing
马背上的Godiva夫人
主人公:戈黛娃夫人Lady Godiva,或称Godgifu,约990年—1067年9月10日
作者:约翰·柯里尔(John Collier)所绘,约1898年
据说大约在1040年,统治考文垂(Coventry)城市的Leofric the Dane伯爵决定向人民征收重税,支持军队出战,令人民的生活苦不堪言。伯爵善良美丽的妻子Godiva夫人眼见民生疾苦,决定恳求伯爵减收徵税,减轻人民的负担。Leofric伯爵勃然大怒,认为Godiva夫人为了这班爱哭哭啼啼的贱民苦苦衷求,实在丢脸。Godiva夫人却回答说伯爵定会发现这些人民是多么可敬。他们决定打赌——Godiva夫人要赤裸身躯骑马走过城中大街,仅以长发遮掩身体,假如人民全部留在屋内,不偷望Godiva夫人的话,伯爵便会宣布减税。翌日早上,Godiva夫人骑上马走向城中,Coventry市所有百姓都诚实地躲避在屋内,令大恩人不至蒙羞。事后,Leofric伯爵信守诺言,宣布全城减税。这就是著名的Godiva夫人传说。
本文转自博客园坚强2002的博客,原文链接:
http://www.cnblogs.com/me-sa/p/elixir_compile_step.html如需转载请自行联系原博主。