[Erlang 0110] Erlang Abstract Format , Part 1

时间:2022-06-01 18:46:40

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

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

[Erlang 0110] Erlang Abstract Format , Part 1

Attribute

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

-module(k).
-compile(export_all).
-compile({parse_transform,print_form}).
-export([test/]).
-url("http://cnblogs.com/me-sa/").
-record(student,{class,id}).
-record(player,{id=,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,见下图
[Erlang 0110] Erlang Abstract Format , Part 1
 

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
说到这里我们可以把概念之间的层次关系梳理出来了:
[Erlang 0110] Erlang Abstract Format , Part 1
 

Run! Run!

 
 上面啰嗦了那么多细节,那从Abstract Format如何到可执行的代码呢?下面我们就完成这个过程:
>  {ok, MTs, _} = erl_scan:string("-module(t).").
{ok,[{'-',},
{atom,,module},
{'(',},
{atom,,t},
{')',},
{dot,}],
}
> {ok, ETs, _} = erl_scan:string("-export([say/0]).").
{ok,[{'-',},
{atom,,export},
{'(',},
{'[',},
{atom,,say},
{'/',},
{integer,,},
{']',},
{')',},
{dot,}],
}
> {ok, FTs, _} = erl_scan:string("say() -> \"hello_world!!\".").
{ok,[{atom,,say},
{'(',},
{')',},
{'->',},
{string,,"hello_world!!"},
{dot,}],
}
> Forms= [begin {ok,R}=erl_parse:parse_form(Item),R end || Item<-[MTs,ETs,FTs]].
[{attribute,,module,t},
{attribute,,export,[{say,}]},
{function,,say,,
[{clause,,[],[],[{string,,"hello_world!!"}]}]}]
> {ok, t, Bin} = compile:forms(Forms).
{ok,t,
<<,,,,,,,,,,,,,,,,,,,
,,,,,,,...>>}
> code:load_binary(t,"nofile",Bin).
{module,t}
> t:say().
"hello_world!!"
>
 好吧,好多好玩的东西还没有说,暂时到这里,下回继续......
 
 
最后小图一张,经历了25年,13季,大侦探波洛的故事结束了,"女士们,先生们,该收场了........."
 

[Erlang 0110] Erlang Abstract Format , Part 1

  

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

  1. &lbrack;Erlang 0111&rsqb; Erlang Abstract Format &comma; Part 2

       上回书,我们说到飞天玉虎蒋伯芳来到蜈蚣岭,不是,重来,上回咱们说到可以在Erlang Shell里面手工构造,加载并调用一个模块.在那个demo里面,我把多个Form单独生成出来,最后放在一起做 ...

  2. &lbrack;Erlang 0129&rsqb; Erlang 杂记 VI

    把之前阅读资料的时候记下的东西,整理了一下. Adding special-purpose processor support to the Erlang VM   P23 简单介绍了Erlang C ...

  3. &lbrack;Erlang 0124&rsqb; Erlang Unicode 两三事 - 补遗

    最近看了Erlang User Conference 2013上patrik分享的BRING UNICODE TO ERLANG!视频,这个分享很好的梳理了Erlang Unicode相关的问题,基本 ...

  4. &lbrack;Erlang 0105&rsqb; Erlang Resources 小站 2013年1月~6月资讯合集

    很多事情要做,一件一件来; Erlang Resources 小站 2013年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/     ...

  5. &lbrack;Erlang 0122&rsqb; Erlang Resources 2014年1月~6月资讯合集

    虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/   ...

  6. Erlang 103 Erlang分布式编程

    Outline 笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明 2014-11-23 A Outl ...

  7. &lbrack;Erlang 0057&rsqb; Erlang 排错利器&colon; Erlang Crash Dump Viewer

    http://www.cnblogs.com/me-sa/archive/2012/04/28/2475556.html Erlang Crash Dump Viewer真的是排错的天兵神器,还记得我 ...

  8. &lbrack;Erlang 0118&rsqb; Erlang 杂记 V

       我在知乎回答问题不多,这个问题: "对你职业生涯帮助最大的习惯是什么?它是如何帮助你的?",我还是主动回答了一下.    做笔记 一开始笔记软件做的不好的时候就发邮件给自己, ...

  9. &lbrack;Erlang 0107&rsqb; Erlang实现文本截断

       抽时间处理一下之前积压的一些笔记.前段时间有网友 @稻草人 问字符串截断的问题"各位大侠 erlang截取字符串一般用哪个函数啊",有人支招用string:substr/3, ...

随机推荐

  1. java 实现一个简易计算器

    import java.util.Scanner;public class Test { public static void main(String[] args) { count(); } pub ...

  2. Java 内存区域和GC机制分析

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  3. RTP协议分析

      目录(?)[-] 第1章     RTP概述 RTP是什么 RTP的应用环境 相关概念 流媒体 第2章     RTP详解 RTP的协议层次 传输层的子层 应用层的一部分 RTP的封装 RTCP的 ...

  4. CentOS7使用VPN上网

    安装epel源 $ sudo yum install epel-release 安装软件包 $ sudo yum install NetworkManager-pptp NetworkManager- ...

  5. java构造函数也可以用private开头

    private 构造函数一般用于Singleton模式,指的是整个应用只有本类的一个对象,一般这种类都有一个类似getInstance()的方法!下面是一个Singleton的例子:public cl ...

  6. python正则表达式入门

    基本概念 使用正则表达式需要import re 表达式前加r避免转义 \d代表数字,\s代表空白字符,\w代表字母+数字. .代表任意单个字符 {m,n}代表前面字符至少出现m次,最多出现n次. (x ...

  7. python3&period;4&plus;selenium爬58同城(一)

    爬取http://bj.58.com/pbdn/0/pn2/中除转转.推广商品以外的产品信息,因为转转和推广的详情信息不规范,需要另外写一个方法存放,后期补上,详情页如下 这周学习了爬虫,但是遇到一些 ...

  8. NYOJ915——&plus;-字符串

    +-字符串 时间限制:1000 ms  |  内存限制:65535 KB 难度:1   描述 Shiva得到了两个只有加号和减号的字符串,字串长度相同.Shiva一次可以把一个加号和它相邻的减号交换. ...

  9. 深度学习(PYTORCH)-2&period;python调用dlib提取人脸68个特征点

    在看官方教程时,无意中发现别人写的一个脚本,非常简洁. 官方教程地址:http://pytorch.org/tutorials/beginner/data_loading_tutorial.html# ...

  10. JVM内核优化

    1.垃圾回收器 JVM垃圾回收器有串行和并行两种. 1.1 Serial收集器(串行,单线程),现在使用较少 Serial一般收集新生代 SerialOld一般收集老年代(采用标记压缩算法) 1.2 ...