Мета-программирование
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.04.04 09:27
Оценка: 10 (4) +1
К обсуждению приглашаются все кому интересно мета-программирование. Особо интересны лица которые уже пытались заниматься этим делом, например, на базе шаблонов C++. Так что не игнорируйте эту тему только от того, что в ней говорится о языке который вы не используете или не любите.

Проект R#
Автор(ы): Чистяков Влад (VladD2)
Дата: 28.01.2004
подошел к стадии когда все технические навороты необходимые для его реализации уже реализованы (есть даже простенький, работающий, прототип метп-кода). Теперь пришла пора более серьезно задуматься над таем что конкретно, и каким образом должно быть в R#.

Напомню, что R# — это система мета-программирования. Более подробно R# описан тут
Автор(ы): Чистяков Влад (VladD2)
Дата: 28.01.2004
. На сегодня R# — это не полноценный компилятор, а всего лишь интеллектуальный препроцессор. В состав R# входит:
1. Парсер C# и его препроцессора.
2. Средства интеграции с VS.NET 2003.

На сегодня R# генерирует код во время записи файла (используя технологию Custom tools).

Парсер R# разбирает код удовлетворяющий синтаксису C# (на сегодня соответствующий спецификации 1.2, но в будущем и 2.0) и строит абстрактное синтаксическое дерево (AST — Abstract Syntactic Tree).

Средства интеграции с VS.NET позволяют осуществлять программную трансформацию кода. Трансформация производится специальными объектами — трансформаторами, которые пишутся прикладным программистом. Объекты трансформации могут быть как специализированными (решать проблему конкретного проекта), или универсальными. Например можно реализовать универсальный трансформатор который позволит реализовать такие фичи как шаблоны, сериализация или O/R Mapping.

R# поддерживает два языка запросов позволяющих отбирать ряд AST-веток. Первый язык запросов — это стилизованный C#, т.е. язык где в качестве логических выражений можно использовать C#. Второй — это XPath.

Ниже приведено описание первого трансформатора который позволяет скопировать члены класса-шаблона в трансформируемый класс (аля примитивный вариант шаблонов C++).

Первое что нужно сделать — это описать трансформацию. Это делается в специальных мета-файлах. Задачи описания указать:
1. Какой трансформатор нужно использовать.
2. Для каких AST-веток (т.е. для каких фрагментов кода) нужно произвести трансформацию. Другими словами задать фильтр трансформируемых веток.
3. Указать параметры, если таковые требуются для трансформатора.

Пока что сам трансформатор должен быть реализован в отдельном файле, но в будущем код трансформации можно будет помещать прямо в проект (при этом будет производиться теневая компиляция), так и прямо внутрь обычного кода. Правда, пока не совсем ясно как отделять мета-код от обычного.

Вот пример описания трансформации:
[
  assembly:
  RsBatchTransform(
    // Алиас трансформатора
    transformer    = "Transform",
    // Тип трансформации (передается трансформаторе)
    transformation = "CopyMembers",
    // Фильтр отбирающий ветки АСТ подлежащие трансформации.
    // Найти все классы у которых установлен атрибут CollectionImpl
    // XPath("//class[CustomAttributeSections/*/*/*[@Name = 'CollectionImpl']]"),
    filter         = GetClasses(CustomAttributes.Contains("CollectionImpl")),
    // Необязательные параметры. В данном случае в параметре передается имя шаблона.
    parametrs      = RsParam("Templates.ElemTypeCollection")) 
]


Таким образом данное описание можно описать на словака как: "Найти все классы помеченные атрибутом "CollectionImpl" и применить к ним трансформацию "CopyMembers". При этом имя трансформатора "Transform", и ему нужно передать в качестве параметра строку "Templates.ElemTypeCollection", которая в данном случае является именем класса-шаблона".

Эта трансформация применяется ко всем файлам проекта и если в файле имеется классы, помеченные атрибутом "CollectionImpl" они подвергаются трансформации.

Трансформатор — это объект реализующий интерфейс:
public interface ITransformer
{
    void Transform(
        // Ветка которую нужно трансформировать
        IAstNode nodeToTransform,
        // имя трансформации. Своего рода имя метода.
        string transformName,
        // параметры которые можно использовать как дополнительную информацию.
        RExpressionCollection peramerts,
        // класс вызывающий трансформатор. Он отвечает за порсинг 
        // и генерацию текста.
        GeneratorMain generator);
}


Вот код трансформатора, о котором идет речь:

namespace Transformers
{
    using Params = RParameterDeclarationExpressionCollection;
    using Param = RParameterDeclarationExpression;

    public class BaseTransformer : ITransformer, INameResolver
    {
        public BaseTransformer()
        {
        }
    
        #region ITransformer Members

        public void Transform(
            IAstNode nodeToTransform, 
            string transformName, 
            RExpressionCollection peramerts,
            GeneratorMain generator)
        {
            if (transformName != "CopyMembers")
                throw new Exception(
                    string.Format("Метод транформации {0} неподдреживается.", 
                    transformName));

            // Получить мета-шаблон.

            if (peramerts.Count != 1)
                throw new Exception("CopyMembers необходимо передать параметр содержащий имя класса-лаблона.");

            RPrimitiveExpression clsNameExpr = peramerts[0] as RPrimitiveExpression;
            if (clsNameExpr == null)
                throw new Exception("Параметр должен содержать полное имя класса-шаблона.");
            string templateName = GeneratorMain.ToString(clsNameExpr);

            RTypeUdt metaUdt = (RTypeUdt)generator.MetaProject.ResolveFullTypeName(
                templateName);

            // Создать глубокую копию мета-шаблона.
            _metaUdt = (RTypeUdt)metaUdt.Clone();

            try
            {
                // Внимание! Парент подключается на время. Иначе не будут работать
                // запросы и т.п. После обработки он должен быть отключен.
                _metaUdt.Parent = nodeToTransform.Parent;

                // Считать все мета-атрибуты шаблона.

                _generator = generator;
                _metaAttrMap.Clear();
                _nodeToTransform = (RTypeUdt)nodeToTransform;

                // Выполнить все мета-действия заданные декларативно.
                ProcessMetaAttribute();

                // Добавить члены мета-шаблона в конечный класс.
                CopyMembers();

                // Удалить все что относится к мета-коду.
            }
            finally
            {
                // Очистка...
                _metaUdt.Parent = null;
                _generator = null;
                _nodeToTransform = null;
            }
        }
...
        void CopyMembers()
        {
            RTypeMemberCollection members = _nodeToTransform.Members;
            foreach (RTypeMember member in _metaUdt.Members)
            {
                // Пропускаем метод если в трансформируемом классе уже имется
                // метода с такой же сигнатурой.
                RMemberMethod method = member as RMemberMethod;
                if (method != null)
                    if (_nodeToTransform.IsMethodDefined(method))
                        continue;

                members.Add(member);
                member.Parent = _nodeToTransform;
            }
        }
...


А вот шаблон используемый при трансформации (именно его члены копируются в трансформируемый класс):
namespace Templates
{
    using System;
    using System.Collections;

    [MetaAttributes(CollectionImpl)]
    [MetaExec(
        // вместо следующих двух запросов можно применить этот:
        // "//*[@TypeName = 'ElemType']"
        XPath("//Type[@TypeName = 'ElemType']")
            && XPath("//ElementType[@TypeName = 'ElemType']"),
        Actions(TypeName = CollectionImpl.ItemType))
    ]
    [MetaExec(
        XPath("//*[@TypeName = 'ElemTypeCollection']"),
        // Благодаря простенькому интерпретатору можно даже 
        // производить вычисления.
        Actions(TypeName = CollectionImpl.ItemType + "Collection"))
    ]
    public class ElemTypeCollection : System.Collections.CollectionBase
    {
        public ElemTypeCollection()
        {
        }

        public ElemTypeCollection(ElemTypeCollection value)
        {
            this.AddRange(value);
        }

        public ElemTypeCollection(ElemType[] value)
        {
            this.AddRange(value);
        }

        public ElemType this[int index]
        {
            get { return List[index] as ElemType; }
            set { List[index] = value; }
        }

        public int Add(ElemType value)
        {
            return List.Add(value);
        }

        public int Add(RExpression value)
        {
            return Add(new RExpressionStatement(value));
        }

        public void AddRange(ElemType[] value)
        {
            InnerList.AddRange(value);
        }

        public void AddRange(ElemTypeCollection value)
        {
            InnerList.AddRange(value);
        }

        public bool Contains(ElemType value)
        {
            return List.Contains(value);
        }

        public void CopyTo(ElemType[] array, int index)
        {
            List.CopyTo(array, index);
        }

        public int IndexOf(ElemType value)
        {
            return List.IndexOf(value);
        }

        public void Insert(int index, ElemType value)
        {
            List.Insert(index, value);
        }

        public void Remove(ElemType value)
        {
            List.Remove(value);
        }

        public void CopyTo(IAstNode[] nodes, int index)
        {
            InnerList.CopyTo(nodes, index);
        }
    }
}


Собственно все действия по трансформации производятся в методе CopyMembers. Как видите он довольно прост.

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

Атрибут MetaExec позволяет выполнить некоторые действия (в данном примере модификацию AST). В первом параметре этого мата-атрибута должен содержаться запрос, отбирающий нужные подветки (корнем является шаблон, к которому применен атрибут), а вторым параметром передается список действий (заключенных в метод Actions). Действия это любые выражения C#. В качестве источника данных можно использовать параметры мета-атрибутов описанных атрибутом MetaAttributes.

Вот пример применения класса подвегающегося трансформации этой трансформацией:
    [CollectionImpl(ItemType = "Test")]
    public class TestCollection
    {
        public TestCollection()
        {
        }

        public TestCollection(Test[] value)
        {
            AddRange(value);
        }


        void SomeMethod()
        {
            int i = 0;
        }
    }


Обратите внимание на метаатрибут "CollectionImpl" задающий параметры трансфорамации (в данном случае указвающий имя типа элемента коллекции). Сам атрибут CollectionImpl описан в мета-шаблоне (см. выше).

А вот код после трансформации:
  public class TestCollection
  {
    public TestCollection()
    {
    }
    public TestCollection(Test[] value)
    {
      AddRange(value);
    }
    void SomeMethod()
    {
      int i = 0;
    }
    public TestCollection(TestCollection value)
    {
      (this).AddRange(value);
    }
    public Test this[int index]
    {
      get
      {
        return List[index] as Test;
      }
      set
      {
        List[index] = value;
      }
    }
    public int Add(Test value)
    {
      return List.Add(value);
    }
    public int Add(RExpression value)
    {
      return Add(new RExpressionStatement(value));
    }
    public void AddRange(Test[] value)
    {
      InnerList.AddRange(value);
    }
    public void AddRange(TestCollection value)
    {
      InnerList.AddRange(value);
    }
    public bool Contains(Test value)
    {
      return List.Contains(value);
    }
    public void CopyTo(Test[] array, int index)
    {
      List.CopyTo(array, index);
    }
    public int IndexOf(Test value)
    {
      return List.IndexOf(value);
    }
    public void Insert(int index, Test value)
    {
      List.Insert(index, value);
    }
    public void Remove(Test value)
    {
      List.Remove(value);
    }
    public void CopyTo(IAstNode[] nodes, int index)
    {
      InnerList.CopyTo(nodes, index);
    }
  }






Это всего лишь один из простеньких примеров более-менее универсального трансформатора.

Теперь о том, что интересно. Интересны все идеи связанные с видением мета-кода (того как он должен выглядеть и т.п.), целями для которых можно его использовать и вообще приветствуются все интересные идеи!
... << RSDN@Home 1.1.3 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.