gpt4 book ai didi

erlang - 抽象形式上的模式匹配

转载 作者:行者123 更新时间:2023-12-02 11:41:35 25 4
gpt4 key购买 nike

免责声明:之所以保留这一点,是因为某些事情可能对其他人有用,但是,它并不能解决我最初尝试做的事情。

目前,我正在尝试解决以下问题:

给定类似{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包含没有自由变量的术语,因此您必须保持将类似 {a, B, {c, D}}的内容更改为 (即填充变量),使用t_from_term / 1,然后遍历返回的数据结构,并使用模块的t_var / 1或其他方式将'_'原子更改为变量。

在解释我最终如何做之前,让我先说明一下问题。

问题

我正在开发一个宠物项目(ErlAOP扩展),准备好后将在SourceForge上托管。基本上,已经存在另一个项目( ErlAOP),通过该项目可以在函数调用之前/之后/周围/周围...等调用(如果感兴趣,请参见 doc)。

我想扩展它以支持在发送/接收级别上注入代码(由于另一个项目)。我已经做到了,但是在托管项目之前,我想做一些改进。

目前,我的实现只是找到send运算符的每个用法或接收表达式,并在前后/前后插入一个函数(由于尾部递归,接收表达式有点麻烦)。我们将此函数称为dmfun(动态匹配函数)。

当消息形式为例如{a,B,{c,D}}正在发送,那么应该在发送之前评估函数do_something / 1。因此,当前实现在每次使用源代码中的send op之前先注入dmfun。 Dmfun将具有以下内容:

case Arg of
{a, B, {c, D}} -> do_something(Arg);
_ -> continue
end


此处Arg可以简单地传递到dmfun / 1,因为您可以访问从源代码生成的表单。

因此,问题在于,任何发送操作符都将在其之前注入dmfun / 1(并且将发送操作的消息作为参数传递)。但是,当发送50,{a,b},[6、4、3]等消息时,这些消息肯定与{a,B,{c,D}}不匹配,因此在send处注入dmfun / 1这些消息是浪费。

我希望能够选择合理的发送操作,例如! {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}在应有的时间不匹配(通过“ match”,我的意思是应该注入dmfun / 1。

我们称其为Alg下面概述的“算法”。我采用的方法是执行Alg(msg_format,msg)和Alg(msg,msg_format)。以下说明仅通过其中之一进行。通过重复相同的操作,仅获得不同的匹配函数( {a, '_', {c, '_'}}而不是 matching_fun(msg_format)),并且仅当Alg(msg_format,msg)或Alg(msg,msg_format)中的至少一个返回true时才注入dmfun / 1结果应该是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'}]}},即正在发送的消息。 (绑定到消息)。
在以下位置执行fill_vars(Msg):

-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)).

在2的输出上执行form_to_term / 1,其中:

form_to_term(Form) -> element(2, erl_eval:exprs([Form], [])).

对3的输出执行term_to_str / 1,其中:

-define(inject_str(FormatStr, TermList), lists:flatten(io_lib:format(FormatStr, TermList))).
term_to_str(Term) -> ?inject_str("~p", [Term]).

执行 {tuple,24,[{atom,24,a},{var,24,'B'},{var,24,'C'}]},其中v(4)是4的输出,而gsub是:(取自 here

gsub(Str,Old,New) -> RegExp = "\\Q"++Old++"\\E", re:replace(Str,RegExp,New,[global, multiline, {return, list}]).

将变量(例如M)绑定到matching_fun(v(5)),其中:

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.

最后,采用用户提供的消息格式(以字符串形式给出),例如MsgFormat =“ {a,B,{c,D}}}”,然后执行:MsgFormatTerm = form_to_term(fill_vars(str_to_form(MsgFormat)))。然后,您可以M(MsgFormatTerm)。


例如用户提供的消息格式为{a,B,{c,D}}和Pid!在代码中找到的{a,B,C}:

2> weaver_ext:fill_vars({tuple,24,[{atom,24,a},{var,24,'B'},{var,24,'C'}]}).
{tuple,24,[{atom,24,a},{string,0,"_"},{string,0,"_"}]}
3> weaver_ext:form_to_term(v(2)).
{a,"_","_"}
4> weaver_ext:term_to_str(v(3)).
"{a,\"_\",\"_\"}"
5> weaver_ext:gsub(v(4), "\"_\"", "_").
"{a,_,_}"
6> M = weaver_ext:matching_fun(v(5)).
#Fun<erl_eval.6.13229925>
7> MsgFormatTerm = weaver_ext:form_to_term(weaver_ext:fill_vars(weaver_ext:str_to_form("{a, B, {c, D}}"))).
{a,"_",{c,"_"}}
8> M(MsgFormatTerm).
true
9> M({a, 10, 20}).
true
10> M({b, "_", 20}).
false

最佳答案

erl_types(HiPE)中具有此功能。

我不确定您是否拥有使用此模块的正确格式的数据。我似乎记得它以Erlang术语作为输入。如果您发现表单问题,您应该可以使用erl_types:t_from_term/1erl_types:t_is_subtype/2进行大多数操作。

很久以前,我最后一次使用它们,而我只进行了测试运行时,而不是编译时。如果您想看一看我的旧代码中的使用模式(不再工作),可以找到它available at github

关于erlang - 抽象形式上的模式匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11134898/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com