Re[8]: Партия, дай порулить
От: Vermicious Knid  
Дата: 07.02.06 17:35
Оценка: 28 (2)
Здравствуйте, IT, Вы писали:

IT>И ещё. Может это конечно уже реализовано.


IT>В генерации класса может участвовать несколько макросов. В том числе для генерации одного и того же метода. В качестве примера можно взять аспекты. Их можно применять пачками как к одному методу, так и к параметрам. При этом возможно, что между ними придётся каким-то образом передавать некоторые параметры. В принципе, одной глобальной внутренней хешь таблицы на все такие вещи было бы достаточно.


Макросы — это обычный код. В том же исходнике, где находится макрос можно разместить модуль(класс в котором все члены по умолчанию статические) и разместить там все что нужно. В качестве иллюстрации привожу мой недописанный(отсутствуют некоторые полезные удобства) макрос для реализации паттерна абстрактная фабрика.

Сначала пример использования:
class Button { }
class Label { }
class TextBox { }
class FlatButton : Button { }
class FlatLabel : Label { }
class FlatTextBox : TextBox { }
class SkinnableButton : Button { }
class VerySpecialSkinnableButton : SkinnableButton { }
class SkinnableLabel : Label { }
class SkinnableTextBox : TextBox { }

abstract class WidgetFactory
    factory [Button, Label, TextBox] { }
class FlatWidgetFactory : WidgetFactory
    factory [FlatButton, FlatLabel, FlatTextBox] { }

class SkinnableWidgetFactory : WidgetFactory
    factory [VerySpecialSkinnableButton, SkinnableLabel, SkinnableTextBox] { }

module Test
{
    Main() : void
    {
        // force flatF to WidgetFactory type
        def flatF = FlatWidgetFactory() : WidgetFactory;
        def skinF = SkinnableWidgetFactory() : WidgetFactory;
        System.Console.WriteLine("{0} {1} {2}",flatF.CreateButton(),flatF.CreateLabel(),flatF.CreateTextBox());
        System.Console.WriteLine("{0} {1} {2}",skinF.CreateButton(),skinF.CreateLabel(),skinF.CreateTextBox());
    }
}


using Nemerle.Compiler;
using System.Collections;
using PT = Nemerle.Compiler.Parsetree;

module FactoryUtility
{
    public abstractFactoriesCache : Hashtable = Hashtable();
}
[Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeTypedMembers, 
                     Nemerle.MacroTargets.Class, 
                     Inherited = true,
                     AllowMultiple = true)]
macro factory(t : TypeBuilder, param)
syntax ("factory",  param )
{
    def classes =
        match (param)
        {
            | <[ [ .. $classList ] ]> => classList;
            | _ => Message.FatalError($"factory : expected (classId0, classId1, ... classIdN), got $(param.ToString())"); []
        }
    // try to bind a list of expressions into typeinfos
    def TypeClassIds(l : list[PT.PExpr]) : list[TypeInfo]
    {
        def typer = Nemerle.Macros.ImplicitCTX();
        def IdToTypeInfo(id)
        {
            match(typer.BindType (id))
            {
                | MType.Class (typeinfo, _) => typeinfo
                | x => Message.FatalError ($"Expected class type, got $(x.ToString)"); null
            }
        }
        Nemerle.Collections.List.Map(l, IdToTypeInfo);
    } 

    /*
        check if current class is abstract
    */
    if (t.IsAbstract)
    {
        /*
            create abstract methods
        */
        FactoryUtility.abstractFactoriesCache.Add(t.FullName, classes);
        // just type check
        _ = TypeClassIds(classes);
        foreach(id in classes)
        {
            t.Define(<[decl: public abstract $(Macros.UseSiteSymbol("Create" + id.ToString()) : name)() : $id;]>);
        }
    }
    else
    {
        /*
            create concrete methods
        */
        // determine base class
        def parent = t.BaseType;
        when (parent == null)
            Message.FatalError($"factory : Concrete factory $(t.FullName) must be inherited from some abstract factory");
        // base class must be a factory class too
        when (!FactoryUtility.abstractFactoriesCache.ContainsKey(parent.FullName))
            Message.FatalError($"factory: $(parent.FullName) is not an abstract factory!");
        // get a list of an abstract products
        def abstract_classes = FactoryUtility.abstractFactoriesCache[parent.FullName] :> list[PT.PExpr];
        // bind concrete product ids to actual typeinfos
        def tys = TypeClassIds(classes);
        // do same for abstract products
        def abstract_tys = TypeClassIds(abstract_classes);
        foreach((id,ty) in Nemerle.Collections.List.Combineb(classes,tys))
        {
            // match concrete product with an abstract product
            def matchSuperType(sup_type)
            { 
                if (sup_type == null)
                    null
                else
                // supType is one of abstract products
                if (abstract_tys.Contains(sup_type))                   
                    sup_type
                else
                    matchSuperType(sup_type.BaseType);
                
            }
            def abstract_product = matchSuperType(ty.BaseType);
            when (abstract_product == null)
                Message.FatalError($"Concrete product $(id.ToString()) doesn't have matching abstract product");
            t.Define(<[decl: public override $(Macros.UseSiteSymbol("Create" + abstract_product.Name) : name)() 
                                : $(Macros.UseSiteSymbol(abstract_product.Name) : name)
                              {
                                $id();
                              }
                      ]>);
        }
    }
}
Re[5]: Партия, дай порулить
От: Vermicious Knid  
Дата: 07.02.06 19:39
Оценка:
Здравствуйте, IT, Вы писали:


IT>Это как раз не проблема. Единственное что интересно, как ребята сделали доступ к метаданным классов компилируемой сборки. Рефлекшина то никакого ещё нет, нужен полностью свой API.


Доступен внутренний API компилятора. Единственная проблема — довольно слабая документация. Мне часто приходилось смотреть сами исходники компилятора и исходники встроенных макросов.

Получить "метаданные" класса не проблема — лишь бы сам класс был известен. Вот пример макроса(выписывает их в консоль во время компиляции):
using System;
using Nemerle.Compiler;
using PT = Nemerle.Compiler.Parsetree;

module Utils
{
    public GetTypeInfo(typename : TyVar) : TypeInfo
    {
        | MType.Class (typeinfo, _) => typeinfo
        | x => Message.FatalError ($"Expected class type, got $(x.ToString)"); null
    }
}

macro GetTypeModifiers(typename)
{
    def typer = Nemerle.Macros.ImplicitCTX();
    def attributes = Utils.GetTypeInfo(typer.BindType(typename)).GetModifiers().GetCustomAttributes();
    foreach (attr in attributes)
        Console.WriteLine(attr.ToString());
    <[]>
}


Вот пример использования:
using System;

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class,
 Inherited=true, AllowMultiple=true)]
public class FooAttribute : Attribute
{
    public this(_name : string)  {}
}

[Foo("foobar;barfoo"), Foo("foo bar"), Serializable]
public class Test
{
    static Main() : void
    {
        GetTypeModifiers(FooAttribute);
        GetTypeModifiers(Test);
    }
}


Выведет:
AttributeUsage (AttributeTargets.Field | AttributeTargets.Class, Inherited = true, AllowMultiple = true)
Serializable
Foo ("foo bar")
Foo ("foobar;barfoo")


Если я не ошибаюсь, то атрибуты во время компиляции представляют собой просто кусочки AST(то есть то, с чем обычно работают макросы в Nemerle) и обработать их достаточно легко.
Re[8]: Партия, дай порулить
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.06 22:37
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

Цитируй, плиз, окуратнее.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Партия, дай порулить
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.06 22:37
Оценка:
Здравствуйте, IT, Вы писали:

IT>Это как раз не проблема. Единственное что интересно, как ребята сделали доступ к метаданным классов компилируемой сборки. Рефлекшина то никакого ещё нет, нужен полностью свой API.


IT>Меня сейчас интересует где и когда будут вызываться макросы и как будут разруливаться конфликты.


Все там ОК. Ты лучше бы скачал сам Нэмерел и глянул бы идущую с ним документацию. Там есть файл ... описывающий что такое макросы, как они работают и на что способны. Вот цитата от туда по интересующему тебя поводу:

5 Macros operating on declarations

Macros can operate not only on expressions, patterns, types, but also on any part of language, like classes, interfaces, other type declarations, methods etc. The syntax for those operations is quite di?erent. Again, we treat language constructs as data objects, which can be transformed, but this is not done entirely with quotations. We use a special API, designed basing on System.Reflection, which is used in .NET for dynamic generation of assemblies. Nemerle type system and operations we are performing on them are not fully compatible with the interface of Reflection (we cannot directly derive from its classes), but are quite similar.
With such a tool we can analyze, generate or change any declaration in the program. For example, dump a de?nition of each data structure to an XML ?le, create serialization methods or automatically generate ?elds or methods from an external description.
Such macros are not called like ordinary functions, but are added as attributes in front of the declaration, similarly as C# attributes.
[SerializeBinary ()]
public module Company 
{
    [ToXML ("Company.xml")]
    public class Employee
    { 
    ...
    }
    
    [FromXML ("Product.xml"), Comparable ()]
    public class Product { }
    
}


5.1 Transforming types


Macros that operate on declarations change them in the imperative fashion. Their ?rst parameter is always an object representing the given declaration.
macro ToXML (ty : TypeBuilder, file : string) {
We can easily list the data contained in the provided object, like ?elds or methods of a class and add a new method using their names.
// Вот так во время компиляции можно получить информацию о типах компилируемого кода
def fields = ty.GetFields (BindingFlags.Instance %|
            BindingFlags.Public %|
            BindingFlags.DeclaredOnly);

    def list_fields =
        List.Map (fields, fun (x) 
        { <[
            xml_writer.WriteAttributeString
            ($(x.Name : string),
            $(x.Name : usesite).ToString ())
        ]> }
    );
    
    // А это дело добавляет метод генерируемый на основании полученного описания.
    ty.Define (
        <[ decl:
                public ToXML () : void
                {
                    def xml_writer = XmlTextWriter ($(file : string), null);
                    { ..$list_fields };
                    xml_writer.Close (); 
                }
        ]>);


With the macro above (perhaps modi?ed to do some additional format¬ting) we can generate serialization methods for any class simply by adding the [ToXML ("file.xml")] attribute.


Дальше там и про АОП рассказываетя.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.