zoukankan      html  css  js  c++  java
  • [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,比如:
     
     {function,18,test,1,
             [{clause,18,[{nil,18}],[],[{atom,19,empty}]},
              {clause,20,[{atom,20,abc}],[],[{string,21,"atom test"}]}]},
    
     
    这段代码就很好玩了,它在形式上已经非常贴近Clojure(好吧,Lisp行不行)里面函数定义的语法了:
    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其实很好的解释了这里等号其实是一个匹配运算.
    
    
    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> 
    
     
    除了上面这些"普通"的东西,当然还有列表解析和二进制数据处理相关的语法,下面通过两个简单的例子看下:

      

    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需要仔细观察下,它的表达形式是:
    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季,大侦探波洛的故事结束了,"女士们,先生们,该收场了........."
     

      

  • 相关阅读:
    C#实现MD5加密,winform c#2005
    关于 "基础连接已经关闭:接收时发生意外错误"
    SERVERPROPERTY方法说明
    Web 设计与开发终极资源大全(上)
    SQL Server:在 SQL Server 2005 中配置数据库邮件,发送邮件
    Web 地理定位(GeoLocation)知识大全
    sql server2005 创建作业问题
    SQL Server 监视事件
    Remoting 如何穿越防火墙
    使用SQL SERVER 2000的全文检索功能
  • 原文地址:https://www.cnblogs.com/me-sa/p/erlang-abstract-format-1.html
Copyright © 2011-2022 走看看