Использование ASTTransformation для «удаления» def для изменения Scope в скрипте Groovy - PullRequest
0 голосов
/ 17 ноября 2011

Как бы странно это не звучало, мне нужно изменить код Groovy, изменив способ создания переменных.

Ранее я решил другую проблему, связанную с ASTTransformation, когда мне нужно было «имитировать» функциональность Use / Category, изменив код для вызова моего специального специального метода.

Теперь мне нужно сделать следующее:

Мне нужен код следующего типа:

def a = "X";
Integer b = 1;

вести себя как этот код:

a = "X";
b = 1;

Разница в том, что второй пример "хранится" в области привязки.

Некоторый контекст:

Я пишу шаблонный движок groovy, где у меня есть некоторый фрагмент кода (предоставленный пользователем), который помещается в метод в groovy class / file, который я затем компилирую. Когда я запускаю фрагмент кода / метод, мне нужно, чтобы его созданные переменные были доступны через Binding-mechanisme.

Я надеюсь, что кто-то может помочь мне в правильном направлении ..

Заранее спасибо.

UPDATE:

(спасибо за отличный ответ)

У меня есть еще один вопрос:

Я думаю, что это может работать, когда я изменяю все объявления, но было бы лучше, если бы я изменил только те, которые находятся в самом внешнем блоке. Пример:

Object aMethod() {
  def a = 1; //should be modified
  def b = 2; //should be modified
  if (expression) {
    def c = 2; //does not need to be modified
  }
}

Знаете ли вы, как можно определить, находится ли объявление в самом крайнем блоке (основном блоке) в теле метода?

-Morten

Ответы [ 2 ]

1 голос
/ 17 ноября 2011

Вам придется заменить выражения expression в AST на BinaryExpression.Вы заметите, что StatementExpression является BinaryExpression, поэтому вам просто нужно скопировать все.Также не забудьте вызвать setSourcePosition на новом узле, используя старый узел, чтобы убедиться, что номера строк правильные.Выражения хранятся в ExpressionStatements, поэтому их не так сложно заменить.

Что касается того, на каком этапе работать ... Я настоятельно рекомендую сделать это до того, как заданы переменные области, тогда у вас меньше работыи вам не придется удалять множество ссылок на переменные в AST позже.Вы можете сделать это, например, в CONVERSION.

Еще одно слово о семантических изменениях, которые вы вводите с этим:

(1) String a = 1;а = 2;в этом примере кода вы получите два раза строку, хранящуюся в.Это в основном потому, что для каждого назначения Groovy будет выполнять приведение к стилю Groovy, что включает преобразование в String.Это сделано, потому что переменная напечатана.Если он не напечатан, таких преобразований не произойдетЭто относится и к «def», но также к переменным в привязке.Это означает, что ваша привязка будет хранить целые числа 1 и 2, а не строки.

(2) Другой момент - это области видимости.Если я вас правильно понимаю, то вы хотите сделать все переменные глобальными.Программы, написанные в контексте наличия только широкой области видимости для переменной, могут тогда вести себя странно.Это может произойти, особенно для переменных в Groovy Closures, если Groovy Closures выполняются не синхронно, а, например, в более поздней, более изолированной точке в вашей программе

(3) Последний пункт - это изменение в использованииСС.Получить значение локальной переменной мы не используем MOP, вместо этого мы делаем это напрямую.Если вы переместите локальные переменные в глобальную область, вам придется пройти через полную MOP, чтобы получить значения.Если в какой-то момент у вас есть элемент программы, который захватывает вызов и повторяет его самостоятельно, у вас будет другая программа.

Возможно, это не имеет значения в вашем случае ... Я просто хотел, чтобы вы былиосознавать эти вещи.

0 голосов
/ 04 декабря 2011

Ваш обновленный вопрос был следующим: знаете ли вы, как можно определить, находится ли объявление в самом крайнем блоке (основном блоке) в теле метода?

Что ж, я бы, вероятно, сделал следующее:самый простой способ.Получите MethodNode из AST и содержащий код с помощью getCode ().Если это ExpressionStatement, то вы меняете его, если он содержит выражение Expression.Если это BlockStatement, то вы просматриваете все операторы в нем и меняете их, если необходимо, как с помощью единственного.

Это решение простое, но, например, не ловит объявление в цикле while..

Если вы согласны с более безопасным, но и более сложным решением, я предлагаю использовать одного из посетителей, предоставляемых Groovy.Вы можете посчитать уровень вложенности, используя int, перезаписав visitBlockStatment, вы увеличите счет на 1. Затем вы перезапишете visitExpressionStatement.Если уровень вложенности равен 0, а инструкция содержит выражение expressionExpression, тогда вы выполняете преобразование, в противном случае - нет ... в этом случае вам также не нужно углубляться в дерево.Теперь для метода ввода, как правило, посещение метода.Если код метода - BlockStatement, то вы начинаете с уровня -1 и просто продолжаете с супер, в противном случае вы начинаете с уровня 0.

Что касается посетителя ... универсальным является ClassCodeVisitor, которыйимеет смысл, как я описал выше, на самом деле, только если вы хотите преобразовать код из нескольких методов.Или вы используете, например, CodeVisistorSupport, но тогда вам нужно иметь логику инициализации этого метода для дополнительного уровня.

...