2012-06-21 3 views
4

Отказ от ответственности: Я сохранил это, потому что некоторые вещи могут быть полезны другим, однако он не решает, что я изначально пытался сделать.Сравнение образцов по абстрактным формам

Сейчас я пытаюсь решить следующее:

Учитывая то, как {а, В, {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, где желаемый сообщение может быть сгенерировано во время выполнения.

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

  2. 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)). 
    
  3. ли form_to_term/1 на 2 по выходу, где:

    form_to_term(Form) -> element(2, erl_eval:exprs([Form], [])). 
    
  4. 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]). 
    
  5. 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}]). 
    
  6. 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. 
    
  7. Наконец, возьмите прилагаемый пользовательский формат сообщения (которая задается в виде строки), например, 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 
+0

Как '{tuple, 17, [{atom, 17, a}, {integer, 17,5}, {var, 17, 'SomeVar'}]}' на форме '{a, B, { c, D}} '? В частности, почему '' SomeVar'' в форме '{c, D}'? –

+0

Привет Эмиль. Я не уверен, что означает «по форме». В принципе, {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

ответ

2

Существует функциональные возможности для этого в erl_types (HIPE).

Я не уверен, что у вас есть данные в правильной форме для использования этого модуля. Кажется, я помню, что в качестве входных данных используются термины Эрланг. Если вы выясните вопрос о форме, вы сможете сделать больше всего, что вам нужно, с erl_types:t_from_term/1 и erl_types:t_is_subtype/2.

Это было давным-давно, что я в последний раз использовал их, и я только когда-либо выполнял свою тестовую проверку, а не время компиляции. Если вы хотите заглянуть в шаблон использования из моего старого кода (он больше не работает), вы можете найти его available at github.

+0

Привет, Даниэль. Спасибо за информацию :) Извините, что так долго отвечал. Работа мешала моему любимому проекту, и я совсем забыл, что я разместил этот вопрос ^^; Я посмотрю, что вы предложили, прежде чем отвечать или принимать. веселит. – justin

+0

Посмотрите даты; вы ответили менее чем за 24 часа. Это был мой ответ, который был отложен. Удачи найти решение и сообщить мне, могу ли я помочь. –

+0

Ты прав, я ворвался в это ^^; Да, erl_types, похоже, может сделать трюк (поэтому я принял этот ответ) ... но это не то, что я использовал. Переменные оказались проблемой при преобразовании в термины Erlang. Простое изменение их на атомы давало мне ложь с t_is_subtype/2 на входах, которые должны были дать мне правду. Я редактировал вопрос, чтобы показать подход, который я принял. Cheers Daniel :) – justin

0

Я не думаю, что это возможно во время компиляции в общем случае. Рассмотрим:

send_msg(Pid, Msg) -> 
    Pid ! Msg. 

Msg будет выглядеть в var, который является совершенно непрозрачной типа. Вы не можете определить, является ли это кортежем или списком или атомом, так как любой может вызвать эту функцию с помощью чего-либо, предоставленного для Msg.

Это было бы намного проще сделать во время выполнения. Каждый раз, когда вы используете оператор !, вместо этого вам нужно вызвать функцию-оболочку, которая пытается сопоставить сообщение, которое вы пытаетесь отправить, и выполняет дополнительную обработку, если шаблон сопоставляется.

+0

Привет, kjw0188. Да, это именно тот подход, который я сейчас принимаю. Теперь я хочу сделать так, чтобы не вводить эту функцию, шаблон которой соответствует Msg, где это совершенно не нужно. В случае с Пидом! Msg ... его нужно было бы вводить, однако, если это Pid! {something, B}, тогда это определенно не будет, так как я ищу что-то вроде формы {a, B, {c, D}} и {something, B} никогда не сможет соответствовать. веселит. – justin

Смежные вопросы