免责声明:我之所以这样做,是因为有些事情可能对其他人有用,但是,它并没有解决我最初尝试过的事情。
现在,我正试图解决以下问题:
给出类似{a,B,{c,D}}之类的内容,我想扫描给予parse_transform / 2的Erlang表单,并找到send运算符(!)的每个用法。然后我想检查发送的消息,并确定它是否适合模式{a,B,{c,D}}。
因此,请考虑找到以下表格:
{op,17,'!',
{var,17,'Pid'},
{tuple,17,[{atom,17,a},{integer,17,5},{var,17,'SomeVar'}]}}]}]}
由于发送的邮件是:
{tuple,17,[{atom,17,a},{integer,17,5},{var,17,'SomeVar'}]}
是{a,5,SomeVar}的编码,这将匹配{a,B,{c,D}}的原始模式。
我不确定我将如何解决这个问题,但您知道任何可能有用的API函数吗?
将给定的{a,B,{c,D}}转换为一种形式是可能的,首先用一些东西代替变量,例如:字符串(并注意到这一点),否则他们将被解除绑定,然后使用:
> erl_syntax:revert(erl_syntax:abstract({a, "B", {c, "D"}})).
{tuple,0,
[{atom,0,a},
{string,0,"B"},
{tuple,0,[{atom,0,c},{string,0,"D"}]}]}
我想在以这样的格式获取它们之后,我可以一起分析它们:
> erl_syntax:type({tuple,0,[{atom,0,a},{string,0,"B"},{tuple,0,[{atom,0,c},string,0,"D"}]}]}).
tuple
%% check whether send argument is also a tuple.
%% then, since it's a tuple, use erl_syntax:tuple_elements/1 and keep comparing in this way, matching anything when you come across a string which was a variable...
我想我最终会遗漏一些东西(例如,识别某些东西而不是其他东西......即使它们应该匹配)。 我可以使用任何API函数来简化此任务吗?至于模式匹配测试运算符或那些沿线的东西,那不存在吗? (即仅在此建议:http://erlang.org/pipermail/erlang-questions/2007-December/031449.html)。
编辑:(这次从头开始解释)
如果你使用t_from_term / 1返回的erl_type(),那么使用以下Daniel建议的erl_types可能是可行的,即t_from_term / 1需要一个没有自由变量的术语,所以你必须保持改变{{ {1}}进入{a, B, {c, D}}
(即填充变量),使用t_from_term / 1然后浏览返回的数据结构并更改' _'原子到变量使用模块的t_var / 1或其他东西。
在解释我最终如何解决之前,让我先说明问题。
问题
我正在开展一个宠物项目(ErlAOP扩展),我准备好在SourceForge上托管它。基本上,另一个项目已经存在(ErlAOP),通过该项目可以在/ etc / around函数调用之前/之后注入代码(如果感兴趣,请参阅doc)。
我想扩展它以支持在发送/接收级别注入代码(因为另一个项目)。我已经完成了这个,但在主持项目之前,我想做一些改进。
目前,我的实现只是发现每次使用send运算符或接收表达式并在/ after / around之前注入一个函数(由于尾递归,接收表达式有一些问题)。让我们调用此函数 dmfun (动态匹配函数)。
用户将指定表格的消息,例如正在发送{a,B,{c,D}},然后在发送之前评估函数do_something / 1。因此,当前实现在源代码中每次使用send op之前注入dmfun。然后Dmfun会有类似的东西:
{a, '_', {c, '_'}}
其中Arg可以简单地传递给dmfun / 1,因为您可以访问源代码生成的表单。
所以问题是任何发送操作符都会在它之前注入dmfun / 1(并且send op' s消息作为参数传递)。但是当发送50,{a,b},[6,4,3]等消息时......这些消息肯定不会匹配{a,B,{c,D}},所以在发送时注入dmfun / 1这些消息是浪费。
我希望能够选择合理的发送操作,例如Pid! {a,5,SomeVar}或Pid! {a,X,SomeVar}。在这两种情况下,注入dmfun / 1是有意义的,因为如果在运行时,SomeVar = {c,50},那么应该评估用户提供的do_something / 1(但是如果SomeVar = 50,那么它不应该,因为我们对{a,B,{c,D}}感兴趣,50与{c,D}不匹配。
我提前写了以下内容。它没有解决我遇到的问题。我最终没有包括这个功能。无论如何我都离开了这个解释,但是如果由我决定,我会完全删除这个帖子......我还在试验,我不认为这里的内容对任何人都有用。
在解释之前,让:
msg_format =用户提供的消息格式,用于确定发送/接收的消息是否有趣(例如{a,B,{c,D}})。
msg =源代码中发送的实际消息(例如Pid!{a,X,Y})。
我在之前的编辑中给出了下面的解释,但后来发现它不会匹配它应该做的一些事情。例如。当msg_format = {a,B,{c,D}}时,msg = {a,5,SomeVar}在它应该的时候不匹配(通过"匹配"我的意思是dmfun / 1应该被注射。
让我们调用"算法"下面概述Alg。我采用的方法是执行Alg(msg_format,msg)和Alg(msg,msg_format)。以下解释仅通过其中一个。通过重复相同的事情只获得不同的匹配函数(case Arg of
{a, B, {c, D}} -> do_something(Arg);
_ -> continue
end
而不是matching_fun(msg_format)
),并且仅在Alg(msg_format,msg)或Alg(msg,msg_format)中的至少一个时注入dmfun / 1返回true,那么结果应该是注入dmfun / 1,其中可以在运行时实际生成所需的消息。
获取您在给予parse_transform / 2的[Forms]中找到的消息表格,例如让我们说你找到:matching_fun(msg)
因此,您可以使用{op,24,'!',{var,24,'Pid'},{tuple,24,[{atom,24,a},{var,24,'B'},{var,24,'C'}]}}
这是要发送的消息。 (绑定到Msg)。
请fill_vars(Msg)在哪里:
{tuple,24,[{atom,24,a},{var,24,'B'},{var,24,'C'}]}
在2&输出上执行form_to_term / 1,其中:
-define(VARIABLE_FILLER, "_").
-spec fill_vars(erl_parse:abstract_form()) -> erl_parse:abstract_form().
%% @doc This function takes an abstract_form() and replaces all {var, LineNum, Variable} forms with
%% {string, LineNum, ?VARIABLE_FILLER}.
fill_vars(Form) ->
erl_syntax:revert(
erl_syntax_lib:map(
fun(DeltaTree) ->
case erl_syntax:type(DeltaTree) of
variable ->
erl_syntax:string(?VARIABLE_FILLER);
_ ->
DeltaTree
end
end,
Form)).
在3&#39输出上执行term_to_str / 1,其中:
form_to_term(Form) -> element(2, erl_eval:exprs([Form], [])).
执行-define(inject_str(FormatStr, TermList), lists:flatten(io_lib:format(FormatStr, TermList))).
term_to_str(Term) -> ?inject_str("~p", [Term]).
,其中v(4)是4的输出,gsub是:(取自here)
gsub(v(4), "\"_\"", "_")
将变量(例如M)绑定到matching_fun(v(5)),其中:
gsub(Str,Old,New) -> RegExp = "\\Q"++Old++"\\E", re:replace(Str,RegExp,New,[global, multiline, {return, list}]).
最后,采用用户提供的消息格式(以字符串形式给出),例如MsgFormat =" {a,B,{c,D}}"和do:MsgFormatTerm = form_to_term(fill_vars(str_to_form(MsgFormat)))。然后你可以M(MsgFormatTerm)。
e.g。用户提供的消息格式= {a,B,{c,D}}和Pid!在代码中找到{a,B,C}:
matching_fun(StrPattern) ->
form_to_term(
str_to_form(
?inject_str(
"fun(MsgFormat) ->
case MsgFormat of
~s ->
true;
_ ->
false
end
end.", [StrPattern])
)
).
str_to_form(MsgFStr) ->
{_, Tokens, _} = erl_scan:string(end_with_period(MsgFStr)),
{_, Exprs} = erl_parse:parse_exprs(Tokens),
hd(Exprs).
end_with_period(String) ->
case lists:last(String) of
$. -> String;
_ -> String ++ "."
end.
答案 0 :(得分:2)
erl_types
(HiPE)中有此功能。
我不确定您是否拥有使用此模块的正确格式的数据。我似乎记得它需要Erlang术语作为输入。如果您发现表单问题,则应该能够通过erl_types:t_from_term/1
和erl_types:t_is_subtype/2
完成所需的大部分工作。
很久以前,我上次使用过这些,而且我只测试过运行时,而不是编译时间。如果你想查看旧代码中的使用模式(不再工作),你可以找到它available at github。
答案 1 :(得分:0)
在一般情况下,我不认为在编译时这是可能的。考虑:
send_msg(Pid, Msg) ->
Pid ! Msg.
Msg
看起来像var
,这是一种完全不透明的类型。您无法判断它是元组还是列表或原子,因为任何人都可以使用为Msg
提供的任何内容调用此函数。
这在运行时更容易做到。每次使用!
运算符时,您都需要调用包装函数,它会尝试匹配您尝试发送的消息,并在模式匹配时执行其他处理。