上回书,我们说到飞天玉虎蒋伯芳来到蜈蚣岭,不是,重来,上回咱们说到可以在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.
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 | [root@nimbus demo]# cat a.erl -module(a). -compile({parse_transform,print_form}). -export([test/0]). test()-> "hello world!" . [root@nimbus 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
[2] Hacking-Erlang http://zh.scribd.com/doc/22451864/Hacking-Erlang
最后小图一张,最早在山东卫视凌晨剧场看过第一季,现在已经14季了, 老员工只有Nicky ,Sara ,Jim了:

分类:
Erlang
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2011-12-10 [Erlang 0020]网页游戏分线到不分线
2007-12-10 Asp.net Ajax:我可以用javascript做些什么?