К обсуждению приглашаются все кому интересно мета-программирование. Особо интересны лица которые уже пытались заниматься этим делом, например, на базе шаблонов 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 >>