[Erlang 0111] Erlang Abstract Format , Part 2

上回书,我们说到飞天玉虎蒋伯芳来到蜈蚣岭,不是,重来,上回咱们说到可以在Erlang Shell里面手工构造,加载并调用一个模块.在那个demo里面,我把多个Form单独生成出来,最后放在一起做compile:forms,是不是可以简单点?我们先看完整的module代码,erl_scan:string之后是什么样子的:

erl_syntax


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

Eshell
V5.10.2  (abort with ^G)

1>
Code =
"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "
.

"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "

2>
{ok,Tokens,_} =erl_scan:
string(Code).

{ok,[{‘-‘,1},

     {atom,1,module},

     {‘(‘,1},

     {atom,1,t},

     {‘)‘,1},

     {dot,1},

     {‘-‘,2},

     {atom,2,export},

     {‘(‘,2},

     {‘[‘,2},

     {atom,2,say},

     {‘/‘,2},

     {integer,2,0},

     {‘]‘,2},

     {‘)‘,2},

     {dot,2},

     {atom,4,say},

     {‘(‘,4},

     {‘)‘,4},

     {‘->‘,4},

     {string,4,"Hello
world!!"
},

     {dot,4}],

    4}

  

可以看到上面的list里面包含了若干Form,erl_scan可以逐行解析代码:


1

2

3

4

5

6

7

8

9

4>
erl_scan:tokens([],Code,1).

{done,{ok,[{‘-‘,1},

           {atom,1,module},

           {‘(‘,1},

           {atom,1,t},

           {‘)‘,1},

           {dot,1}],

          2},

      "-export([say/0]).\n\nsay()
-> \"Hello world!!\". "
}

  

当然还有更简单的方式,dynamic_compile 项目把这些事情都做了,还考虑了更为复杂的情况,比如头文件,宏,注释,record之类的, https://github.com/JacobVorreuter/dynamic_compile/blob/master/src/dynamic_compile.erl 下面就是dynamic_compile使用的一个例子:


1

2

3

4

5

6

7

8

Eshell
V5.10.2  (abort with ^G)

1>
Code =
"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "
.

"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "

2>
dynamic_compile:load_from_string(Code).

{module,t}

3>
t:say().

"Hello
world!!"

4>

  

上面拼字符串的方法看起来丑?你可以选择erl_syntax,下面我们用比较"优雅"的方法去创建t模块:


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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

Eshell
V5.10.2  (abort with ^G)

1>
M  = erl_syntax:attribute(erl_syntax:atom(module),[erl_syntax:atom(t)]).

{tree,attribute,

      {attr,0,[],none},

      {attribute,{tree,atom,{attr,0,[],none},module},

                 [{tree,atom,{attr,0,[],none},t}]}}

2>
MF =  erl_syntax:revert(M).

{attribute,0,module,t}

3>
E = erl_syntax:attribute(erl_syntax:atom(export),[erl_syntax:list([erl_syntax:arity_qualifier(erl_syntax:atom(say),erl_syntax:integer(0))])]).

{tree,attribute,

    {attr,0,[],none},

    {attribute,

        {tree,atom,{attr,0,[],none},export},

        [{tree,list,

             {attr,0,[],none},

             {list,

                 [{tree,arity_qualifier,

                      {attr,0,[],none},

                      {arity_qualifier,

                          {tree,atom,{attr,0,[],none},say},

                          {tree,integer,{attr,0,[],none},0}}}],

                 none}}]}}

4>
ExportForm = erl_syntax:revert(E).

{attribute,0,export,[{say,0}]}

5>

5>

5>
Clause= erl_syntax:clause([],[],[erl_syntax:atom(hello_world)]).

{tree,clause,

      {attr,0,[],none},

      {clause,[],none,[{tree,atom,{attr,0,[],none},hello_world}]}}

6>

6>
Function =  erl_syntax:function(erl_syntax:atom(say),[Clause]).

{tree,function,

      {attr,0,[],none},

      {func,{tree,atom,{attr,0,[],none},say},

            [{tree,clause,

                   {attr,0,[],none},

                   {clause,[],none,

                           [{tree,atom,{attr,0,[],none},hello_world}]}}]}}

7>
FunctionForm = erl_syntax:revert(Function).

{function,0,say,0,[{clause,0,[],[],[{atom,0,hello_world}]}]}

8> 
{ok, Mod, Bin1} = compile:forms([MF,ExportForm, FunctionForm]).

{ok,t,

    <<70,79,82,49,0,0,1,208,66,69,65,77,65,116,111,109,0,0,0,

      57,0,0,0,6,1,116,...>>}

9>
t:say().

**
exception error: undefined function t:say/0

10>
code:load_binary(Mod, [], Bin1).

{module,t}

11>
t:say().

hello_world

12>

  

Erlang Shared Data using mochiglobal里面mochiglobal 就是用这种方法实现的,不过,不过,又会有人提意见了,这编写难度也太大了,能折中一下吗?好吧,请出下一位嘉宾smerl

我曾经介绍过开源项目smerl,其定位就是Simple Metaprogramming for Erlang, 我们可以从这份代码里面学到erl_scan erl_parse erl_eval更灵活的应用,项目地址:http://code.google.com/p/smerl/


1

2

3

4

5

6

7

test_smerl()
->

      M1
= smerl:
new(foo),

      {ok,
M2} = smerl:add_func(M1,
"bar()
-> 1 + 1."
),

      smerl:compile(M2),

      foo:bar(),  
% returns 2``

      smerl:has_func(M2,
bar, 0). % returns
true

  

parse_transform

在上篇文章里面,我需要反复生成 Abstract Format,开始手工搞了一下,后来不胜其烦就使用了一下parse_transform.这东西是干什么用的呢?

{parse_transform,Module}

Causes the parse transformation function Module:parse_transform/2 to be applied to the parsed code before the code is checked for errors.

对,它就是允许你在检查错误之前插入自己的逻辑,把那些"奇怪的东西"变成正常的东西,当然你可以选择什么都不做,仅仅打印,看代码:


1

2

3

4

5

6

-module(print_form).

-export([parse_transform/2]).

parse_transform(Forms,
_Options) ->

  io:format("forms:
~p~n"
,
[Forms]),

  Forms.

  

下面,我们写一个简单的模块a.erl,然后编译它,看输出:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

[[email protected]
demo]# cat a.erl

-module(a).

-compile({parse_transform,print_form}).

-export([test/0]).

 

test()->

  "hello
world!"
.

[[email protected]
demo]# erlc -o . -pa . a.erl 

forms:
[{attribute,1,file,{
"a.erl",1}},

        {attribute,1,module,a},

        {attribute,3,export,[{test,0}]},

        {function,5,test,0,[{clause,5,[],[],[{string,6,"hello
world!"
}]}]},

        {eof,10}]

  

好吧,感冒,鼻子堵得难受,先到这里吧

参考资料:

[0] erl_syntax  http://erlang.org/doc/man/erl_syntax.html

[1] Erlang: How to Create and Compile Module in Run-time

http://vas.io/blog/2013/03/23/erlang-how-to-create-and-compile-module-in-run-time/

[2] Hacking-Erlang http://zh.scribd.com/doc/22451864/Hacking-Erlang

最后小图一张,最早在山东卫视凌晨剧场看过第一季,现在已经14季了, 老员工只有Nicky ,Sara ,Jim了:

上回书,我们说到飞天玉虎蒋伯芳来到蜈蚣岭,不是,重来,上回咱们说到可以在Erlang Shell里面手工构造,加载并调用一个模块.在那个demo里面,我把多个Form单独生成出来,最后放在一起做compile:forms,是不是可以简单点?我们先看完整的module代码,erl_scan:string之后是什么样子的:

erl_syntax


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

Eshell
V5.10.2  (abort with ^G)

1>
Code =
"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "
.

"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "

2>
{ok,Tokens,_} =erl_scan:
string(Code).

{ok,[{‘-‘,1},

     {atom,1,module},

     {‘(‘,1},

     {atom,1,t},

     {‘)‘,1},

     {dot,1},

     {‘-‘,2},

     {atom,2,export},

     {‘(‘,2},

     {‘[‘,2},

     {atom,2,say},

     {‘/‘,2},

     {integer,2,0},

     {‘]‘,2},

     {‘)‘,2},

     {dot,2},

     {atom,4,say},

     {‘(‘,4},

     {‘)‘,4},

     {‘->‘,4},

     {string,4,"Hello
world!!"
},

     {dot,4}],

    4}

  

可以看到上面的list里面包含了若干Form,erl_scan可以逐行解析代码:


1

2

3

4

5

6

7

8

9

4>
erl_scan:tokens([],Code,1).

{done,{ok,[{‘-‘,1},

           {atom,1,module},

           {‘(‘,1},

           {atom,1,t},

           {‘)‘,1},

           {dot,1}],

          2},

      "-export([say/0]).\n\nsay()
-> \"Hello world!!\". "
}

  

当然还有更简单的方式,dynamic_compile 项目把这些事情都做了,还考虑了更为复杂的情况,比如头文件,宏,注释,record之类的, https://github.com/JacobVorreuter/dynamic_compile/blob/master/src/dynamic_compile.erl 下面就是dynamic_compile使用的一个例子:


1

2

3

4

5

6

7

8

Eshell
V5.10.2  (abort with ^G)

1>
Code =
"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "
.

"-module(t).\n-export([say/0]).\n\nsay()
-> \"Hello world!!\". "

2>
dynamic_compile:load_from_string(Code).

{module,t}

3>
t:say().

"Hello
world!!"

4>

  

上面拼字符串的方法看起来丑?你可以选择erl_syntax,下面我们用比较"优雅"的方法去创建t模块:


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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

Eshell
V5.10.2  (abort with ^G)

1>
M  = erl_syntax:attribute(erl_syntax:atom(module),[erl_syntax:atom(t)]).

{tree,attribute,

      {attr,0,[],none},

      {attribute,{tree,atom,{attr,0,[],none},module},

                 [{tree,atom,{attr,0,[],none},t}]}}

2>
MF =  erl_syntax:revert(M).

{attribute,0,module,t}

3>
E = erl_syntax:attribute(erl_syntax:atom(export),[erl_syntax:list([erl_syntax:arity_qualifier(erl_syntax:atom(say),erl_syntax:integer(0))])]).

{tree,attribute,

    {attr,0,[],none},

    {attribute,

        {tree,atom,{attr,0,[],none},export},

        [{tree,list,

             {attr,0,[],none},

             {list,

                 [{tree,arity_qualifier,

                      {attr,0,[],none},

                      {arity_qualifier,

                          {tree,atom,{attr,0,[],none},say},

                          {tree,integer,{attr,0,[],none},0}}}],

                 none}}]}}

4>
ExportForm = erl_syntax:revert(E).

{attribute,0,export,[{say,0}]}

5>

5>

5>
Clause= erl_syntax:clause([],[],[erl_syntax:atom(hello_world)]).

{tree,clause,

      {attr,0,[],none},

      {clause,[],none,[{tree,atom,{attr,0,[],none},hello_world}]}}

6>

6>
Function =  erl_syntax:function(erl_syntax:atom(say),[Clause]).

{tree,function,

      {attr,0,[],none},

      {func,{tree,atom,{attr,0,[],none},say},

            [{tree,clause,

                   {attr,0,[],none},

                   {clause,[],none,

                           [{tree,atom,{attr,0,[],none},hello_world}]}}]}}

7>
FunctionForm = erl_syntax:revert(Function).

{function,0,say,0,[{clause,0,[],[],[{atom,0,hello_world}]}]}

8> 
{ok, Mod, Bin1} = compile:forms([MF,ExportForm, FunctionForm]).

{ok,t,

    <<70,79,82,49,0,0,1,208,66,69,65,77,65,116,111,109,0,0,0,

      57,0,0,0,6,1,116,...>>}

9>
t:say().

**
exception error: undefined function t:say/0

10>
code:load_binary(Mod, [], Bin1).

{module,t}

11>
t:say().

hello_world

12>

  

Erlang Shared Data using mochiglobal里面mochiglobal 就是用这种方法实现的,不过,不过,又会有人提意见了,这编写难度也太大了,能折中一下吗?好吧,请出下一位嘉宾smerl

我曾经介绍过开源项目smerl,其定位就是Simple Metaprogramming for Erlang, 我们可以从这份代码里面学到erl_scan erl_parse erl_eval更灵活的应用,项目地址:http://code.google.com/p/smerl/


1

2

3

4

5

6

7

test_smerl()
->

      M1
= smerl:
new(foo),

      {ok,
M2} = smerl:add_func(M1,
"bar()
-> 1 + 1."
),

      smerl:compile(M2),

      foo:bar(),  
% returns 2``

      smerl:has_func(M2,
bar, 0). % returns
true

  

parse_transform

在上篇文章里面,我需要反复生成 Abstract Format,开始手工搞了一下,后来不胜其烦就使用了一下parse_transform.这东西是干什么用的呢?

{parse_transform,Module}

Causes the parse transformation function Module:parse_transform/2 to be applied to the parsed code before the code is checked for errors.

对,它就是允许你在检查错误之前插入自己的逻辑,把那些"奇怪的东西"变成正常的东西,当然你可以选择什么都不做,仅仅打印,看代码:


1

2

3

4

5

6

-module(print_form).

-export([parse_transform/2]).

parse_transform(Forms,
_Options) ->

  io:format("forms:
~p~n"
,
[Forms]),

  Forms.

  

下面,我们写一个简单的模块a.erl,然后编译它,看输出:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

[[email protected]
demo]# cat a.erl

-module(a).

-compile({parse_transform,print_form}).

-export([test/0]).

 

test()->

  "hello
world!"
.

[[email protected]
demo]# erlc -o . -pa . a.erl 

forms:
[{attribute,1,file,{
"a.erl",1}},

        {attribute,1,module,a},

        {attribute,3,export,[{test,0}]},

        {function,5,test,0,[{clause,5,[],[],[{string,6,"hello
world!"
}]}]},

        {eof,10}]

  

好吧,感冒,鼻子堵得难受,先到这里吧

参考资料:

[0] erl_syntax  http://erlang.org/doc/man/erl_syntax.html

[1] Erlang: How to Create and Compile Module in Run-time

http://vas.io/blog/2013/03/23/erlang-how-to-create-and-compile-module-in-run-time/

[2] Hacking-Erlang http://zh.scribd.com/doc/22451864/Hacking-Erlang

最后小图一张,最早在山东卫视凌晨剧场看过第一季,现在已经14季了, 老员工只有Nicky ,Sara ,Jim了:

时间: 2024-11-06 23:08:51

[Erlang 0111] Erlang Abstract Format , Part 2的相关文章

[Erlang 0110] Erlang Abstract Format , Part 1

Erlang Abstract Format并不难懂,只是枯燥一点罢了,如果把Abstract Format的文档翻译出来,其实就是Erlang教科书中语法入门的部分. Erlang Abstract Format实际上是用Erlang代码的AST,下面通过一些真切的实例代码了解一下它的一些细节. 首先,Erlang Abstract Format里面包含一些概念,我会在下面的描述中把涉及到的概念字体加粗.请注意概念之间的层次关系.Erlang代码本身使用非常扁平的module组织,每一个mod

[Erlang 0129] Erlang 杂记 VI

把之前阅读资料的时候记下的东西,整理了一下. Adding special-purpose processor support to the Erlang VM   P23 简单介绍了Erlang Compiler和Beam文件格式; The Erlang Compiler in short 章节提到了 Core Erlang 这个之前有提到过: [Erlang 0120] Know a little Core Erlang http://www.cnblogs.com/me-sa/p/know

[Erlang 00124] Erlang Unicode 两三事 - 补遗

最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本上把 Using Unicode in Erlang 讲解了一遍.再次学习了一下,整理成文字,补充一些 [Erlang 0062] Erlang Unicode 两三事 遗漏掉的内容. 视频在这里: http://www.youtube.com/watch?v=M6hPLCA0F-Y PDF在这里:

[Erlang危机]Erlang In Danger 序言

原创文章,转载请注明出处:服务器非业余研究http://blog.csdn.net/erlib 作者Sunface?? Introduction On Running Software 运行时软件 There's something rather unique in Erlang in how it approaches failure compared to most other programming languages. There's this common way of thinkin

[Erlang 0122] Erlang Resources 2014年1月~6月资讯合集

虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索. 小站地址: http://site.douban.com/204209/ 1月   114 RR Elixir with José Valim by CHARLES MAX WOOD on JULY 17, 2013 http://rubyrogues.com/114-rr-elixir-with-jose-valim/ "The Erlang Runtime System"

[Erlang 0057] Erlang 排错利器: Erlang Crash Dump Viewer

http://www.cnblogs.com/me-sa/archive/2012/04/28/2475556.html Erlang Crash Dump Viewer真的是排错的天兵神器,还记得我们之前曾经讨论过[Erlang 0013]抓取Erlang进程运行时信息 [Erlang 0012]Erlang Process input queue ,下面是我梳理的"How to interpret the Erlang crash dumps"的文档; 很多人在问有什么工具可以打开

[erlang 001]erlang中的错误及异常处理

一. erlang中的错误 1. 分类     1) 编译错误:主要是编译器检测出的代码语法错误:     2) 逻辑错误:是指程序没有完成预期的工作,属于开发人员的问题:     3) 运行时错误:是指erlang运行时抛出的错误,比如对非数据类型执行算术运算,erlang运行时会捕获异常,并抛出.在erlang中,这类异常的类型为error:     4) 用户代码生成的错误:是指通过exit/1或者throw/1生成. 2. 异常 我们把运行时错误以及用户抛出的错误称为异常(excepti

Erlang 102 Erlang并发编程 - should be done in 2014-11-09

笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明2014-11-02 A outline,1 Agenda 0 范围 Erlang的现实世界建模方式 Erlang进程创建 Erlang进程设计模式 Erlang进程错误处理 1 Erlang Concurrency Modeling Philosophy Armstrong在[2]中跳出程序语言在处理并发应用场景时洋洋洒洒的急迫性.跃跃欲试的一站式

Erlang 103 Erlang分布式编程.- 缺2~4

Outline 笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明 2014-11-23 A Outline   A 1.1-1.22014-12-08 A 1.3 Agenda 0范围 节点和通信 基本分布式编程模块 empd进程 套接字编程 1 Erlang节点和通信 1.1节点 一个Erlang节点是已命名的(named)的正在运行的Erlang运行时系统(erts). 多个节点可以运行在一台