[Erlang 0110] Erlang Abstract Format , Part 1

Erlang Abstract Format并不难懂,只是枯燥一点罢了,如果把Abstract Format的文档翻译出来,其实就是Erlang教科书中语法入门的部分. Erlang Abstract Format实际上是用Erlang代码的AST,下面通过一些真切的实例代码了解一下它的一些细节.

首先,Erlang Abstract Format里面包含一些概念,我会在下面的描述中把涉及到的概念字体加粗.请注意概念之间的层次关系.Erlang代码本身使用非常扁平的module组织,每一个module是由一系列forms组成的.这些forms分成两大类:attributes函数声明 见下图.

Attribute

attributes相对比较简单,我们先从一个只有attributes没有任何函数声明的module开始

-module(k).
-compile(export_all).
-compile({parse_transform,print_form}).
-export([test/0]).
-url("http://cnblogs.com/me-sa/").
-record(student,{class,id}).
-record(player,{id=0,name=[],level}).

上面代码对应的Abstract Format如下:

[{attribute,1,file,{"k.erl",1}},
        {attribute,1,module,k},
        {attribute,2,compile,export_all},
        {attribute,4,export,[{test,0}]},
        {attribute,5,url,"http://cnblogs.com/me-sa/"},
        {attribute,6,record,
                   {student,[{record_field,6,{atom,6,class}},
                             {record_field,6,{atom,6,id}}]}},
        {attribute,7,record,
                   {player,[{record_field,7,{atom,7,id},{integer,7,0}},
                            {record_field,7,{atom,7,name},{nil,7}},
                            {record_field,7,{atom,7,level}}]}},
        {eof,10}]

我们可以一一对照上面思维导图,可以看到上面每一行代码包括record定义在内在Abstract Format层面看都是attribute.里面不断出现的数字是代码所在行数,这个信息是非常重要的,在编译时提示代码出错行,运行时报错包含代码行数都要用到它.Erlang最新版报错的时候已经包含了出错的代码所在行,而之前这个功能是没有的,工作在蛮荒纪的erlanger,搞了一个smart_exceptions的项目来实现这个功能: https://github.com/thomasl/smart_exceptions/tree/master/stable 言归正传,上面需要细说的form是record的定义,在有record字段初始值的情况,结构会稍复杂一点:{record_field,LINE,Name,Value}.比如player的id=0,对应的Form是{record_field,7,{atom,7,id},{integer,7,0}},id的初始值0是一个字面常量,记作{integer,7,0},在Erlang
Abstract Format文档里面字面常量被称为Atomic literals,包含四种:atom integer string float,见下图

Function  declarations

说过了attribute,下面我们看下函数声明部分,这里有一个概念Patterns,它特指function或fun的参数列表.我们做几个简单的方法出来:

-module(a).
-compile(export_all).
-export([test/0]).
 -record(student,{class,id}).
-record(player,{id=0,name=[],level}).

test()->
  "hello world!".

test(a,[1,2]) ->
  "a:[1,2]";
test(12.5,100)->
   "test".

test([]) ->
    empty;
test(abc) ->
    "atom test".

foo(a)->
   {b,100}.

bar({1,2},12)->
   [1,2,3,4,5,6].

k(Num)  when Num >1000 ->
   bigger_than_100;
k(Num) ->
   whatever.

call(1000)->
  k(1000);
call(1002)->
  erlang:now().       

它对应的Abstract Format是:

[{attribute,1,file,{"a.erl",1}},
        {attribute,1,module,a},
        {attribute,2,compile,export_all},
        {attribute,4,export,[{test,0}]},
        {attribute,6,record,
         {student,
          [{record_field,6,{atom,6,class}},{record_field,6,{atom,6,id}}]}},
        {attribute,7,record,
         {player,
          [{record_field,7,{atom,7,id},{integer,7,0}},
           {record_field,7,{atom,7,name},{nil,7}},
           {record_field,7,{atom,7,level}}]}},
        {function,10,test,0,[{clause,10,[],[],[{string,11,"hello world!"}]}]},
        {function,13,test,2,
         [{clause,13,
           [{atom,13,a},
            {cons,13,{integer,13,1},{cons,13,{integer,13,2},{nil,13}}}],
           [],
           [{string,14,"a:[1,2]"}]},
          {clause,15,
           [{float,15,12.5},{integer,15,100}],
           [],
           [{string,16,"test"}]}]},
        {function,18,test,1,
         [{clause,18,[{nil,18}],[],[{atom,19,empty}]},
          {clause,20,[{atom,20,abc}],[],[{string,21,"atom test"}]}]},
        {function,24,foo,1,
         [{clause,24,
           [{atom,24,a}],
           [],
           [{tuple,25,[{atom,25,b},{integer,25,100}]}]}]},
        {function,27,bar,2,
         [{clause,27,
           [{tuple,27,[{integer,27,1},{integer,27,2}]},{integer,27,12}],
           [],
           [{cons,28,
             {integer,28,1},
             {cons,28,
              {integer,28,2},
              {cons,28,
               {integer,28,3},
               {cons,28,
                {integer,28,4},
                {cons,28,
                 {integer,28,5},
                 {cons,28,{integer,28,6},{nil,28}}}}}}}]}]},
        {function,30,k,1,
         [{clause,30,
           [{var,30,‘Num‘}],
           [[{op,30,‘>‘,{var,30,‘Num‘},{integer,30,1000}}]],
           [{atom,31,bigger_than_100}]},
          {clause,32,[{var,32,‘Num‘}],[],[{atom,33,whatever}]}]},
        {function,36,call,1,
         [{clause,36,
           [{integer,36,1000}],
           [],
           [{call,37,{atom,37,k},[{integer,37,1000}]}]},
          {clause,38,
           [{integer,38,1002}],
           [],
           [{call,39,{remote,39,{atom,39,erlang},{atom,39,now}},[]}]}]},
        {eof,41}]

看上面的函数声明,function里面包含一个或者多个function clauses,比如:


1

2

3

{function,18,test,1,

        [{clause,18,[{nil,18}],[],[{atom,19,empty}]},

         {clause,20,[{atom,20,abc}],[],[{string,21,"atom
test"
}]}]},

这段代码就很好玩了,它在形式上已经非常贴近Clojure(好吧,Lisp行不行)里面函数定义的语法了:


1

2

3

4

5

6

7

8

9

10

Clojure
1.4.0

user=>
(defn make_a_set

          ([x]
#{x})

          ([x,y]
#{x,y}))

#‘user/make_a_set

user=>
(make_a_set 12)

#{12}

user=>
(make_a_set 12 23)

#{12
23}

user=>

再看一下函数调用,erlang:now().这一句的调用对应的是: [{call,39,{remote,39,{atom,39,erlang},{atom,39,now}},[]}]}]} 看到这里能想起来Erlang里面对Local CallRemote Call的定义了吧. 上面的代码里面还可以看到Guard的表达形式:

 [[{op,30,‘>‘,{var,30,‘Num‘},{integer,30,1000}}]].

除了function clasuse之外,还有 if clausescase clauses and catch clauses 就不再细说.深入函数体内部就是各种表达式, 方法体里面包含了一些表达式Expressions,ExpressionsPatterns表示方式是一样的.比如A
= lists:seq(1,10).这样一个表达式,它的Abstract Format其实很好的解释了这里等号其实是一个匹配运算.



1

2

3

4

5

6

7

8

9

10

7> 
E=
fun(Code)->
{_,Tokens,_}=erl_scan:string(Code),rp(erl_parse:parse_exprs(Tokens))
end.

#Fun<erl_eval.6.80484245>

8>
E(
"
A = lists:seq(1,10)."
).

{ok,[{match,1,

            {var,1,‘A‘},

            {call,1,

                  {remote,1,{atom,1,lists},{atom,1,seq}},

                  [{integer,1,1},{integer,1,10}]}}]}

ok

10>

除了上面这些"普通"的东西,当然还有列表解析和二进制数据处理相关的语法,下面通过两个简单的例子看下:

  


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

Eshell
V5.10.2  (abort
with ^G)

1> 
E=
fun(Code)->
{_,Tokens,_}=erl_scan:string(Code),rp(erl_parse:parse_exprs(Tokens))
end.

#Fun<erl_eval.6.80484245>

2>
E(
"[Item
|| Item<- [1,2,3,4],Item>2 ]."
).

{ok,[{lc,1,

         {var,1,‘Item‘},

         [{generate,1,

                    {var,1,‘Item‘},

                    {cons,1,

                          {integer,1,1},

                          {cons,1,

                                {integer,1,2},

                                {cons,1,{integer,1,3},{cons,1,{integer,1,4},{nil,1}}}}}},

          {op,1,‘>‘,{var,1,‘Item‘},{integer,1,2}}]}]}

ok

3>

 

7> 
E=
fun(Code)->
{_,Tokens,_}=erl_scan:string(Code),rp(erl_parse:parse_exprs(Tokens))
end.

#Fun<erl_eval.6.80484245>

8>
E(
"<<A:8,B/binary>>
= <<1,2,3,4>>."
).

{ok,[{match,1,

            {bin,1,

                 [{bin_element,1,{var,1,‘A‘},{integer,1,8},default},

                  {bin_element,1,{var,1,‘B‘},default,[binary]}]},

            {bin,1,

                 [{bin_element,1,{integer,1,1},default,default},

                  {bin_element,1,{integer,1,2},default,default},

                  {bin_element,1,{integer,1,3},default,default},

                  {bin_element,1,{integer,1,4},default,default}]}}]}

ok

 

对于Erlang数据结构中的王者List需要仔细观察下,它的表达形式是:


1

2

3

4

5

6

7

4>
E(
"[a,b,c,d].").

{ok,[{cons,1,

           {atom,1,a},

           {cons,1,

                 {atom,1,b},

                 {cons,1,{atom,1,c},{cons,1,{atom,1,d},{nil,1}}}}}]}

ok

说到这里我们可以把概念之间的层次关系梳理出来了:

Run! Run!

上面啰嗦了那么多细节,那从Abstract Format如何到可执行的代码呢?下面我们就完成这个过程:

3>  {ok, MTs, _} = erl_scan:string("-module(t).").
{ok,[{‘-‘,1},
     {atom,1,module},
     {‘(‘,1},
     {atom,1,t},
     {‘)‘,1},
     {dot,1}],
    1}
4>  {ok, ETs, _} = erl_scan:string("-export([say/0]).").
{ok,[{‘-‘,1},
     {atom,1,export},
     {‘(‘,1},
     {‘[‘,1},
     {atom,1,say},
     {‘/‘,1},
     {integer,1,0},
     {‘]‘,1},
     {‘)‘,1},
     {dot,1}],
    1}
5> {ok, FTs, _} = erl_scan:string("say() -> \"hello_world!!\".").
{ok,[{atom,1,say},
     {‘(‘,1},
     {‘)‘,1},
     {‘->‘,1},
     {string,1,"hello_world!!"},
     {dot,1}],
    1}
6>  Forms= [begin {ok,R}=erl_parse:parse_form(Item),R end || Item<-[MTs,ETs,FTs]].
[{attribute,1,module,t},
{attribute,1,export,[{say,0}]},
{function,1,say,0,
           [{clause,1,[],[],[{string,1,"hello_world!!"}]}]}]
7> {ok, t, Bin} = compile:forms(Forms).
{ok,t,
    <<70,79,82,49,0,0,1,224,66,69,65,77,65,116,111,109,0,0,0,
      45,0,0,0,5,1,116,...>>}
8> code:load_binary(t,"nofile",Bin).
{module,t}
9> t:say().
"hello_world!!"
10> 

好吧,好多好玩的东西还没有说,暂时到这里,下回继续......

最后小图一张,经历了25年,13季,大侦探波洛的故事结束了,"女士们,先生们,该收场了........."

  

时间: 2024-10-25 10:46:21

[Erlang 0110] Erlang Abstract Format , Part 1的相关文章

[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

[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). 多个节点可以运行在一台