Отказ от ответственности: Я сохранил это, потому что некоторые вещи могут быть полезны другим, однако он не решает, что я изначально пытался сделать.Сравнение образцов по абстрактным формам
Сейчас я пытаюсь решить следующее:
Учитывая то, как {а, В, {C, D}} Я хочу, чтобы сканировать Erlang форм, приведенных в parse_transform/2 и найти каждое использование оператора отправки (!). Затем я хочу проверить отправленное сообщение и определить, будет ли он соответствовать шаблону {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'}]}
который является кодирование {а, 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).
Edit: (Объясняя вещи с самого начала на этот раз)
Использование erl_types, как Даниэль говорит ниже, вероятно, выполнимо, если вы поиграйте с erl_type() возвращенного t_from_term/1 т.е. t_from_term/1 принимает без каких-либо свободных переменных, поэтому вам нужно было бы изменить что-то вроде {a, B, {c, D}}
на {a, '_', {c, '_'}}
(т. е. заполнить переменные), использовать t_from_term/1, а затем пройти через возвращаемую структуру данных и сменить атомы «_» на переменные, используя t_var модуля/1 или что-то.
Прежде чем объяснять, как я об этом подумал, позвольте мне сформулировать проблему немного лучше.
Проблема
Я работаю над проектом для домашних животных (расширение ErlAOP), который я буду хостинг на SourceForge, когда будете готовы. В принципе, еще один проект уже существует (ErlAOP), через который можно вводить код до/после/вокруг/etc ... вызовы функций (см. doc, если это интересно).
Я хотел расширить это, чтобы поддерживать инъекцию кода на уровне отправки/получения (из-за другого проекта).Я уже сделал это, но перед тем, как приступить к проекту, я хотел бы внести некоторые улучшения.
В настоящее время моя реализация просто находит каждое использование оператора отправки или принимает выражение и вводит функцию до/после/вокруг (получаемые выражения имеют немного gotcha из-за хвостовой рекурсии). Назовем эту функцию dmfun (функция динамического совпадения).
Пользователь будет указывать, что когда сообщение формы, например. {a, B, {c, D}}, тогда функция do_something/1 должна быть оценена до отправки. Таким образом, текущая реализация внедряет dmfun перед каждым использованием команды send в исходном коде. Dmfun бы тогда что-то вроде:
case Arg of
{a, B, {c, D}} -> do_something(Arg);
_ -> continue
end
где Arg просто может быть передан dmfun/1, потому что у вас есть доступ к формам, генерируемых из исходного кода.
Таким образом, проблема заключается в том, что любой оператор посылает перед ним запрос dmfun/1 (и передается сообщение send op в качестве параметра). Но при отправке сообщений типа 50, {a, b}, [6, 4, 3] и т. Д. Эти сообщения, конечно, не будут соответствовать {a, B, {c, D}}, поэтому инъекция 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} не будет соответствовать, когда это необходимо (под «совпадением» я подразумеваю, что dmfun/1 следует ввести.
Давайте назовем «алгоритм», описанный ниже Alg. Подход, который я принял, состоял в том, чтобы выполнить Alg (msg_format, msg) и Alg (msg, msg_format). Объяснение ниже только проходит через один из них. Повторяя одно и то же, (matching_fun(msg_format)
вместо matching_fun(msg)
) и впрыскивание dmfun/1, только если хотя бы один из Alg (msg_format, msg) или Alg (msg, msg_format) возвращает true, тогда результатом будет инъекция dmfun/1, где желаемый сообщение может быть сгенерировано во время выполнения.
Возьмите форму сообщения, которую вы найдете в [Формах], предоставленной parse_transform/2, например.скажем, вы найдете:
{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).Do fill_vars (МВП), где:
-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)).
ли form_to_term/1 на 2 по выходу, где:
form_to_term(Form) -> element(2, erl_eval:exprs([Form], [])).
Do term_to_str/1 на 3 по выходу, где:
-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 является: (взято из here)gsub(Str,Old,New) -> RegExp = "\\Q"++Old++"\\E", re:replace(Str,RegExp,New,[global, multiline, {return, list}]).
BIND переменную (например, М) к 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
Как '{tuple, 17, [{atom, 17, a}, {integer, 17,5}, {var, 17, 'SomeVar'}]}' на форме '{a, B, { c, D}} '? В частности, почему '' SomeVar'' в форме '{c, D}'? –
Привет Эмиль. Я не уверен, что означает «по форме». В принципе, {a, 5, SomeVar} важна для меня, если она найдена в коде src, потому что, во время выполнения SomeVar может оказаться {c, 9}, например, и поэтому {a, 5, {c, 9}} будет соответствовать оригиналу {a, B, {c, D}}.Поэтому из ввода {a, B, {c, D}} моя программа должна распознавать, что форма, подобная {tuple, 17, [{atom, 17, a}, {integer, 17,5}, { var, 17, 'SomeVar'}]}, возможно, совпадают во время выполнения (цель заключается в том, чтобы вводить код в этих точках). – justin