之前遇到過把字符串解析成為Erlang數據項的問題, 參見: [Erlang 0021] From String To Erlang Code
現在我們繼續上文的話題,看看如何動態執行Erlang表達式,首先把方法簡單封裝一下:
eval(S) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Parsed} = erl_parse:parse_exprs(Scanned),
{value, Value,_} = erl_eval:exprs(Parsed,[]),
Value.
在Shell里面練習一下,為了簡單就直接在Shelll里面定義fun使用了:
Eshell V5.9 (abort with ^G)
1> F=fun(S) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Parsed} = erl_parse:parse_exprs(Scanned),
{value, Value,_} = erl_eval:exprs(Parsed,[]),
Value end.
#Fun<erl_eval.6.111823515>
2> F("erlang:now()").
** exception error: no match of right hand side value
{error,{1,erl_parse,["syntax error before: ",[]]}}
3> F("erlang:now().").
{1330,525826,164710}
4> F("A=2."). %%綁定了A為2
2
5> F("B=2."). %%綁定了B為22
6> F("B=3."). %%綁定B為3
3
7> A.
* 1: variable 'A' is unbound
8> B.
* 1: variable 'B' is unbound
9> B=3.
3
10> F("B=3.").
3
11> F("B=31.").
31
12> F("A+B.").
** exception error: {unbound_var,'A'}
看第一個異常的位置,這是由於 F("erlang:now()").執行的語句並沒有以"."結束,修改為F("erlang:now().").正常輸出了時間;緊接着下面嘗試綁定變量,通過連續綁定為B兩次值可以知道這樣操作的變量在Shell中是沒有上下文的,在Shell中定義的變量也不能被使用.然后我們看最后一個表達式,在這個表達式里面有超過一個的變量,直接執行會有變量未綁定的異常,怎么消除這個異常呢?
當然我們可以這樣:
13> F("A=12,B=3,A*B.").
36
14> F("A=12,B=33,A*B.").
396
15>
消除上面的異常實際上就是要維護變量的綁定信息,在erl_eval模塊可以找到和綁定相關的內容,在官方文檔中,我們可以看到Binding的數據結構是怎樣的:
bindings() = [{name(), value()}]
binding_struct() = orddict:orddict()
orddict() = [{Key :: term(), Value :: term()}]
官方文檔地址:
http://www.erlang.org/doc/man/erl_eval.html#type-binding_struct
http://www.erlang.org/doc/man/orddict.html#type-orddict
http://www.erlang.org/doc/man/erl_eval.html#type-binding_struct
http://www.erlang.org/doc/man/orddict.html#type-orddict
練習一下幾個綁定相關的方法:
Eshell V5.9 (abort with ^G)
1> erl_eval:new_bindings().
[]
2> erl_eval:bindings([{'B',23}]).
[{'B',23}]
3> erl_eval:bindings([{'B',23},{'A',34}]).
[{'B',23},{'A',34}]
4> B=[{'B',23},{'A',34}].
[{'B',23},{'A',34}]
5> erl_eval:bindings('B',[{'B',23},{'A',34}]).
** exception error: undefined function erl_eval:bindings/2
6> erl_eval:binding('B',[{'B',23},{'A',34}]).
{value,23}
7> erl_eval:add_binding('C',34,[{'B',23},{'A',34}]).
[{'B',23},{'A',34},{'C',34}]
8> erl_eval:add_binding('C',34,[{'B',23},{'A',34}]).
[{'B',23},{'A',34},{'C',34}]
9> erl_eval:del_binding('C',[{'B',23},{'A',34}]).
[{'B',23},{'A',34}]
10> erl_eval:del_binding('B',[{'B',23},{'A',34}]).
[{'A',34}]
11>
怎么使用變量綁定呢? mryufeng http://blog.yufeng.info/archives/tag/erl_token 給了一個例子:
eval(Str,Binding) ->
{ok,Ts,_} = erl_scan:string(Str),
Ts1 = case lists:reverse(Ts) of
[{dot,_}|_] -> Ts;
TsR -> lists:reverse([{dot,1} | TsR])
end,
{ok,Expr} = erl_parse:parse_exprs(Ts1),
erl_eval:exprs(Expr, Binding).
前面已經熟悉了綁定的數據結構,所以我們直接進入Shell實踐:
Eshell V5.9 (abort with ^G)
1> erl_eval:new_bindings().
[]
2> F =fun(Str,Binding) ->
{ok,Ts,_} = erl_scan:string(Str),
Ts1 = case lists:reverse(Ts) of
[{dot,_}|_] -> Ts;
TsR -> lists:reverse([{dot,1} | TsR])
end,
{ok,Expr} = erl_parse:parse_exprs(Ts1),
erl_eval:exprs(Expr, Binding) end.
#Fun<erl_eval.12.111823515>
3> F("A=23.",[]).
{value,23,[{'A',23}]}
4> F("A+B.",[{'B',23}]).
** exception error: {unbound_var,'A'}
5> F("12+B.",[{'B',23}]).
{value,35,[{'B',23}]}
有什么用呢?
[1] erl -eval
Scans, parses and evaluates an arbitrary expression Expr during system initialization. If any of these steps fail (syntax error, parse error or exception during evaluation), Erlang stops with an error message. Here is an example that seeds the random number generator:
% erl -eval '{X,Y,Z}' = now(), random:seed(X,Y,Z).'
This example uses Erlang as a hexadecimal calculator:
% erl -noshell -eval 'R = 16#1F+16#A0, io:format("~.16B~n", [R])' \\
-s erlang halt
BF
eval(Filename) -> ok | {error, Reason}
eval(Filename, Bindings) -> ok | {error, Reason}
Reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from Filename. The actual result of the evaluation is not returned; any expression sequence in the file must be there for its side effect. Returns one of the following:
[3] 用Erlang實現領域特定語言 http://www.infoq.com/cn/articles/erlang-dsl
[4]一個有趣的項目Erlang Web Shell :
erlwsh
https://github.com/killme2008/erlwsh.
[5] 當然也可以解決一些小問題,比如這個
erlang字符串替換問題-字符串格式化
動態拼接字符串的時候一些變量在字符串多個位置出現,除了使用正則表達式模塊re之外,還可以這樣做:
F =fun(Str,Binding) ->
{ok,Ts,_} = erl_scan:string(Str),
Ts1 = case lists:reverse(Ts) of
[{dot,_}|_] -> Ts;
TsR -> lists:reverse([{dot,1} | TsR])
end,
{ok,Expr} = erl_parse:parse_exprs(Ts1),
erl_eval:exprs(Expr, Binding) end.
6> F("lists:concat(["haha",B,"ok!"]).",[{'B',23}]).
* 1: syntax error before: haha
6> F("lists:concat(['haha',B,'ok!']).",[{'B',23}]).
{value,"haha23ok!",[{'B',23}]}
7> F("lists:concat(['國家',B,'ok!']).",[{'B',23}]).
{value,"國家23ok!",[{'B',23}]}
9> F("lists:concat(['國家',B,'ok!',D,B,'hello ',C]).",[{'B',23},{'C',world},{'D',2012}]).
{value,"國家23ok!201223hello world",
[{'B',23},{'C',world},{'D',2012}]}
[6] erlang-string-lambda
項目地址: https://github.com/debasishg/erlang-string-lambda
可以做的事情很多,如果有局限,那就是我們的想象力了,晚安