[Erlang 0021] From String To Erlang Code

团队的兄弟遇到一个问题,如何把字符串动态解析成为Erlang数据结构(Erlang term)? 比如"[{1,2},{2,3},{3,4}]"转成[{1,2},{2,3},{3,4}]

From String To Erlang Term

http://blog.yufeng.info/archives/tag/erl_eval 余锋在<erlang动态解释>这篇文章中给出了解决方案,他分析的是init.erl模块;按照文章的思路我们在shell里面试一下:

Eshell V5.8.2 (abort with ^G)
1> {ok, Scan1, _} = erl_scan:string("[a,b,c].").
{ok,[{'[',1},{atom,1,a},{',',1},{atom,1,b},{',',1},{atom,1,c},{']',1},{dot,1}],1}
2> {ok,P}=erl_parse:parse_exprs(Scan1).
{ok,[{cons,1,{atom,1,a},{cons,1,{atom,1,b},{cons,1,{atom,1,c},{nil,1}}}}]}
3> erl_eval:exprs(P, []) .
{value,[a,b,c],[]}
4>

红色标注的部分就是我们想要的结果,注意erl_scan:string(Exp).接受的参数是一个合法的表达式,必须以.结尾,代表一个表达式的结束,否则文法检查过不去;看一下输出的结果里面.符号被解析为{dot,1};这个问题最关键的部分就已经解决了,还有一个相关的问题就是如何把一个[{1,2},{2,3},{3,4}]转成字符串?这个当然要在io_lib里面去寻找答案,可以这样做: lists:flatten(io_lib:write([{1,2},{2,3},{3,4}])). 结果为"[{1,2},{2,3},{3,4}]"我们做一个完整的例子:

1> lists:flatten(io_lib:write([{1,2},{2,3},{3,4}])).
"[{1,2},{2,3},{3,4}]"
2> S= lists:flatten(io_lib:write([{1,2},{2,3},{3,4}])).
"[{1,2},{2,3},{3,4}]"
3> E=S++".". %%添加结束符
"[{1,2},{2,3},{3,4}]."
4> {ok, Scan1, _} = erl_scan:string(E).
{ok,[{'[',1},{'{',1},{integer,1,1},{',',1},{integer,1,2},{'}',1},{',',1},{'{',1},{integer,1,2},{',',1},{integer,1,3},{'}',1},{',',1},{'{',1},{integer,1,3},{',',1},{integer,1,4},{'}',1},{']',1},{dot,1}], 1}

5> {ok,P}=erl_parse:parse_exprs(Scan1).
{ok,[{cons,1,{tuple,1,[{integer,1,1},{integer,1,2}]}, {cons,1,{tuple,1,[{integer,1,2},{integer,1,3}]},
{cons,1,{tuple,1,[{integer,1,3},{integer,1,4}]},{nil,1}}}}]}
6> erl_eval:exprs(P, []) .
{value,[{1,2},{2,3},{3,4}],[]}
7>

仅仅是解析Erlang Term 也可以这样:

list_to_term(String) ->
{ok, T, _} = erl_scan:string(String++"."),
case erl_parse:parse_term(T) of
{ok, Term} ->
Term;
{error, Error} ->
Error
end.

From String To Erlang Code

这个问题可以泛化为字符串解析为Erlang代码并执行?同样的问题在.net中,我们可以这样解决:

using System;
using IronPython.Hosting; //缺少引用的去下载一个: http://ironpython.codeplex.com/
using Microsoft.Scripting.Hosting;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
ScriptEngine engine = Python.CreateEngine();
var Result = engine.Execute("2*(1+3)");
Console.WriteLine(Result);
Console.ReadLine();
}
}
}

 

还是使用上面的erl_scan erl_parse erl_eval基础设施,我们可以很容易实现:

Eshell V5.8.2 (abort with ^G)
1> E=fun(S) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Parsed} = erl_parse:parse_exprs(Scanned),
erl_eval:exprs(Parsed,[]) end.
#Fun<erl_eval.6.13229925>
2> E("R=1+2*3.").
{value,7,[{'R',7}]}
3> E("A=2,B=3,A+B.").

{value,5,[{'A',2},{'B',3}]}

那如果是"M=K+L."这样的表达式呢?如何呢?

4> E("M=K+L.").
** exception error: {unbound_var,'K'}

我们直接在shell里面执行报的错误一致,只不过错误信息格式变化一下:* 1: variable 'K' is unbound,那么如何动态给变量绑定数值呢?这样我们可以更灵活控制表达式和值, http://www.trapexit.org/String_Eval 给了一个这样的例子:

 

-module(test).

-export([test/0]).

test()->
    %% Create a code string with unbound variables 'A' and 'B'
    String="Results=A+B/2.",
    
    %% Scan the code into tokens
    {ok,ErlTokens,_}=erl_scan:string(String),
    io:format("ErlTokens are ~p~n",[ErlTokens]),

    %% Now parse the tokens into the abstract form
    {ok,ErlAbsForm}=erl_parse:parse_exprs(ErlTokens),
    io:format("ErlAbsForm are ~p~n",[ErlAbsForm]),

    %% Now we need to bind values to variable 'A' and 'B'
    Bindings=erl_eval:add_binding('A',20,erl_eval:new_bindings()),
    NewBindings=erl_eval:add_binding('B',45,Bindings),
    io:format("The bindings are ~p~n",[erl_eval:bindings(NewBindings)]),

    %% Now evaluate the string
    io:format("Going into erl_eval:exprs~n",[]),
    {value,Value,_}=erl_eval:exprs(ErlAbsForm,NewBindings),
    io:format("Value is ~p~n",[Value]).

You can compile and run this in the shell:

(arrian@psyduck)17> c(test).
{ok,test}
(arrian@psyduck)18> test:test().
ErlTokens are [{var,1,'Results'},
               {'=',1},
               {var,1,'A'},
               {'+',1},
               {var,1,'B'},
               {'/',1},
               {integer,1,2},
               {dot,1}]
ErlAbsForm are [{match,1,
                       {var,1,'Results'},
                       {op,1,
                           '+',
                           {var,1,'A'},
                           {op,1,'/',{var,1,'B'},{integer,1,2}}}}]
The bindings are [{'A',20},{'B',45}]
Going into erl_eval:exprs
Value is 42.5000
ok
(arrian@psyduck)19>            

Note: If you bind variables that don't exist in the code string/token set/abstract form then when you erl_eval the abstract form will simply silently ignore your additional bindings

 

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

  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

 最后顺便提一句,如何把{abc}作为字符串写入到文件中呢?  lists:flatten(io_lib:format("~p",[{a,b,c}])).

--The End--

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值