免責事項:いくつかのことが他の人に役立つかもしれないので、私はこれを維持しました、しかし、それは私が最初にやろうとしたことを解決しません。
今、私は以下を解決しようとしています:
{a、B、{c、D}}のようなものが与えられた場合、parse_transform / 2に与えられたErlangフォームをスキャンして、送信演算子(!)の使用法を見つけたいと思います。次に、送信されているメッセージを確認し、パターン{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)。
編集:(今回は最初から説明します)
{a, B, {c, D}}
ダニエルが以下に示唆するようにerl_typesを使用することは、t_from_term / 1によって返されるerl_type()をいじくり回す場合、おそらく実行可能です{a, '_', {c, '_'}}
。 )、t_from_term / 1を使用してから、返されたデータ構造を調べ、モジュールのt_var/1などを使用して「_」アトムを変数に変更します。
私がどうやってそれをやったのかを説明する前に、問題をもう少しよく述べさせてください。
問題
準備ができたらSourceForgeでホストするペットプロジェクト(ErlAOP拡張機能)に取り組んでいます。基本的に、別のプロジェクト(ErlAOP)がすでに存在します。このプロジェクトを介して、関数呼び出しの前/後/周囲などにコードを挿入できます(興味がある場合はドキュメントを参照してください)。
これを拡張して、送信/受信レベルでのコードの注入をサポートしたかった(別のプロジェクトのため)。私はすでにこれを行っていますが、プロジェクトをホストする前に、いくつかの改善を行いたいと思います。
現在、私の実装では、send演算子またはreceive式の使用をそれぞれ検出し、関数を前後/周囲に挿入します(末尾再帰のため、receive式には少し問題があります)。この関数をdmfun(動的一致関数)と呼びましょう。
ユーザーは、{a、B、{c、D}}などの形式のメッセージが送信されるときに、送信が行われる前に関数do_something/1を評価する必要があることを指定します。したがって、現在の実装では、ソースコードでsendopを使用するたびに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}}と一致しないため、送信時に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)を実行することでした。以下の説明は、これらのうちの1つのみを通過します。同じことを繰り返して(matching_fun(msg_format)
の代わりにmatching_fun(msg)
)異なるマッチング関数を取得し、Alg(msg_format、msg)またはAlg(msg、msg_format)の少なくとも1つがtrueを返した場合にのみ、dmfun / 1を注入すると、結果は注入になります。 dmfun / 1の場合、実行時に目的のメッセージを実際に生成できます。
parse_transform / 2に与えられた[Forms]で見つけたメッセージフォームを取ります。たとえば、次のように見つけます。
{op,24,'!',{var,24,'Pid'},{tuple,24,[{atom,24,a},{var,24,'B'},{var,24,'C'}]}}
つまり{tuple,24,[{atom,24,a},{var,24,'B'},{var,24,'C'}]}
、送信されているメッセージを受け取ります。(Msgにバインド)。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]).
Do
gsub(v(4), "\"_\"", "_")
、ここで、v(4)は4の出力であり、gsubは:(ここから取得)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