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]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 13:57
Оценка: 22 (1)
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


СТ>>Акхм. Не уверен в своей правильной интерпретации выражения "компилятор генераторы подхватывает".


IT>Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.


Пока еще нет. Но я подкинул авторам такую идею. Работа уже идет.


СТ>>Я так понимаю, ты хочешь узнать, насколько легко можно будет повторить функциональность BLTookit на немерловских макросах? И чем тебя не устроил MacroUsage?


IT>Надо писать две лишних строчки в каждом классе, вместо одного названия базового класса. Две лишних строчки очень легко забыть написать.


Это да, Согласен
--
Re[2]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 13:16
Оценка: +1
Здравствуйте, Сергей Туленцев, Вы писали:

Может какой-нибудь новый модификатор для класса можно забульбенить?
В общем, надо разбираться как у них компилятор генераторы подхватывает. Их MacroUsage для атрибутов явно не достаточно.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 02:33
Оценка:
Навеяно Nemerle рулит! :)
Автор: VladD2
Дата: 02.02.06


Интересный получился топик и довольно познавательный. Сразу видно, что за дело профессионально взялись розовые береты
В связи с этим у меня к вам, господа нимерлисты (или как вас теперь ), имеется вопрос.

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

К сожалению, у многих из нас не хватает времени не только на то, чтобы что-то пощупать своими руками, но даже на то, чтобы просто успевать читать форумы RSDN и следить за последними веяниями в индустрии. По-этому, прежде чем самому засучить рукава, уповаую на вас

Суть вопроса. Как я уже сказал выше, меня интересуют прежде всего возможности языка по генерации кода. Вот что я могу сегодня делать на C# с помощью подручных средств. Возьмём, например, такой класс:

public abstract class Test : EditableObject
{
  public abstract int    ID   { get; set; }
  public abstract string Name { get; set; }
}

Этого объявления вполне достаточно, чтобы run-time генератор BLToolkit создал на основании его примерно вот такой код:

namespace EditableObjects.EditableObjectTest.BLToolkitExtension
{
    using BLToolkit.EditableObjects;
    using BLToolkit.Reflection;
    using BLToolkit.TypeBuilder;
    using EditableObjects;
    using System;
    using System.Collections;
    using System.Reflection;

    public class Test : EditableObjectTest.Test, IEditable, IMemberwiseEditable, IPrintDebugState
    {
        // Fields
        private EditableValue<int>    _ID;
        private EditableValue<string> _name;
                
        private static PropertyInfo _ID_$propertyInfo;
        private static PropertyInfo _name_$propertyInfo;

        // Methods
        static Test()
        {
            _ID_$propertyInfo   = TypeHelper.GetPropertyInfo(typeof(EditableObjectTest.Test), "ID",   typeof(int),    Type.EmptyTypes);
            _name_$propertyInfo = TypeHelper.GetPropertyInfo(typeof(EditableObjectTest.Test), "Name", typeof(string), Type.EmptyTypes);
        }

        public Test()
        {
            this._name = new EditableValue<string>("");
        }

        public Test(InitContext)
        {
            this._name = new EditableValue<string>("");
        }

        public override int ID
        {
            get { return this._ID.Value; }
            set
            {
                this._ID.Value = value;
                ((IPropertyChanged)this).OnPropertyChanged(_ID_$propertyInfo);
            }
        }

        public override string Name
        {
            get { return this._name.get_Value(); }
            set
            {
                this._name.set_Value((string) value);
                ((IPropertyChanged)this).OnPropertyChanged(_name_$propertyInfo);
            }
        }

        void IEditable.AcceptChanges()
        {
            this._ID.AcceptChanges();
            this._name.AcceptChanges();
        }

        bool IEditable.IsDirty
        {
            get
            {    
                bool flag1 = this._ID.IsDirty;
                        
                if (!flag1)
                    flag1 = this._name.IsDirty;

                return flag1;
            }
        }

        void IEditable.RejectChanges()
        {
            this._ID.RejectChanges();
            this._name.RejectChanges();
        }

        bool IMemberwiseEditable.AcceptMemberChanges(PropertyInfo propertyInfo, string memberName)
        {
            bool flag1 = this._ID.AcceptMemberChanges(_ID_$propertyInfo, memberName);

            if (!flag1)
                flag1 = this._name.AcceptMemberChanges(_name_$propertyInfo, memberName);

            return flag1;
        }

        void IMemberwiseEditable.GetDirtyMembers(PropertyInfo propertyInfo, ArrayList list)
        {
            this._ID.  GetDirtyMembers(_ID_$propertyInfo,   list);
            this._name.GetDirtyMembers(_name_$propertyInfo, list);
        }

        bool IMemberwiseEditable.IsDirtyMember(PropertyInfo propertyInfo, string memberName, ref bool isDirty)
        {
            bool flag1 = this._ID.IsDirtyMember(_ID_$propertyInfo, memberName, ref isDirty);

            if (!flag1)
                flag1 = this._name.IsDirtyMember(_name_$propertyInfo, memberName, ref isDirty);

            return flag1;
        }

        bool IMemberwiseEditable.RejectMemberChanges(PropertyInfo propertyInfo, string memberName)
        {
            bool flag1 = this._ID.RejectMemberChanges(_ID_$propertyInfo, memberName);

            if (!flag1)
                flag1 = this._name.RejectMemberChanges(_name_$propertyInfo, memberName);

            return flag1;
        }

        void IPrintDebugState.PrintDebugState(PropertyInfo propertyInfo, ref string str)
        {
            this._ID.  PrintDebugState(_ID_$propertyInfo,   ref str);
            this._name.PrintDebugState(_name_$propertyInfo, ref str);
        }
    }
}

Т.е. мы имеем следующие вещи:

1. Внутренную реализацию свойств как EditableValue<int> и EditableValue<string>.
2. Реализацию интерфейса IEditable, включая методы AcceptChanges, RejectChanges и флаг IsDirty.
3. Генерацию события OnPropertyChanged при изменении свойств.
4. Ещё кое-что по мелочи.

Естественно, всё это дело не захардкожено. Генератор берёт информацию о том что и где генерировать из атрибутов, которыми по уши обвешан базовый класс EditableObject. В результате мы имеем почти 100% декларацию и, как результат, минимум работы.

Вопрос. Можно ли достичь подобного уровня декларативности на Nemerle?

Если это принципиально возможно, то не исключено, что в BLToolkit в скором времени может появится соответствующая поддержка
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 09:09
Оценка:
Здравствуйте, IT, Вы писали:

IT>Естественно, всё это дело не захардкожено. Генератор берёт информацию о том что и где генерировать из атрибутов, которыми по уши обвешан базовый класс EditableObject. В результате мы имеем почти 100% декларацию и, как результат, минимум работы.


IT>Вопрос. Можно ли достичь подобного уровня декларативности на Nemerle?


IT>Если это принципиально возможно, то не исключено, что в BLToolkit в скором времени может появится соответствующая поддержка

Принципиально возможно. В стандартной поставке идет пример макроса, который создает прокси методы. Например, так:

public interface IMath
{
    Add(...) : void;
    Mul(...) : void;
}

public class Math : IMath
{

}

public class SomeClass
{
    [DesignPatters.Proxy(IMath)]
    private _math : Math;

}

public module MathApp()
{
   public Main() : void
   {
       def m = SomeClass();
       
       Console.WriteLine(m.Add(5, 6));
    }
}

Писал по памяти, мог напутать, но идея, надеюсь, понятна.

Ссылки по теме

Macros tutorial
Defining types from inside macros
Design patterns
--
Re[2]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 12:55
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

СТ>Принципиально возможно. В стандартной поставке идет пример макроса, который создает прокси методы. Например, так:


Прокси я посмотрел. Коряво это всё как-то
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 13:24
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


СТ>>Принципиально возможно. В стандартной поставке идет пример макроса, который создает прокси методы. Например, так:


IT>Прокси я посмотрел. Коряво это всё как-то


В смысле, коряво?
А как тебе атрибут Singletone? Практически вообще ничего не надо писать.
В ближайшие пару дней попробую набросать код для генерации наследников (от абстрактных классов) с какой-то функциональностью.
--
Re[3]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 13:28
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


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

IT>В общем, надо разбираться как у них компилятор генераторы подхватывает. Их MacroUsage для атрибутов явно не достаточно.

Акхм. Не уверен в своей правильной интерпретации выражения "компилятор генераторы подхватывает".
Я так понимаю, ты хочешь узнать, насколько легко можно будет повторить функциональность BLTookit на
немерловских макросах? И чем тебя не устроил MacroUsage?
--
Re[4]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 13:35
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

СТ>Акхм. Не уверен в своей правильной интерпретации выражения "компилятор генераторы подхватывает".


Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.

СТ>Я так понимаю, ты хочешь узнать, насколько легко можно будет повторить функциональность BLTookit на немерловских макросах? И чем тебя не устроил MacroUsage?


Надо писать две лишних строчки в каждом классе, вместо одного названия базового класса. Две лишних строчки очень легко забыть написать.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re: Партия, дай порулить
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.02.06 13:45
Оценка:
Здравствуйте, IT, Вы писали:

IT>Суть вопроса. Как я уже сказал выше, меня интересуют прежде всего возможности языка по генерации кода. Вот что я могу сегодня делать на C# с помощью подручных средств.


А что понимается под подручными средствами (извини, я не совсем в курсе BLToolkit)?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[2]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:20
Оценка:
Здравствуйте, eao197, Вы писали:

IT>>Суть вопроса. Как я уже сказал выше, меня интересуют прежде всего возможности языка по генерации кода. Вот что я могу сегодня делать на C# с помощью подручных средств.


E>А что понимается под подручными средствами (извини, я не совсем в курсе BLToolkit)?


BLToolkit и понимается. Вот ещё вариант генерации кода — Генерация вызова сохранённых процедур. PersonAccessor — класс, полностью генерируемый библиотекой в рантайм. Содержимое методов генерируется на основе их сигнатуры.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Партия, дай порулить
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.02.06 14:30
Оценка:
Здравствуйте, IT, Вы писали:

IT>BLToolkit и понимается. Вот ещё вариант генерации кода — Генерация вызова сохранённых процедур. PersonAccessor — класс, полностью генерируемый библиотекой в рантайм. Содержимое методов генерируется на основе их сигнатуры.


Я про то, чем ты сигнатуры получаешь? Через Reflection? Если так, то Nemerle -- это же .NET, там должно быть доступно все то, что доступно для C#. Может быть те же инструменты подойдут и для Nemerle?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[6]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:30
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

IT>>Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.


СТ>Пока еще нет. Но я подкинул авторам такую идею. Работа уже идет.


Вау, круто Может им ещё каких идей наподкидывать?
Например, для обхода торомозов рефлекшин, полезна возможность генерации класса аксессора для указанного класса. Т.е. нужно сгенерировать совершенно отдельный набор классов не трогая исходный.
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:32
Оценка:
Здравствуйте, IT, Вы писали:

IT>Например, для обхода торомозов рефлекшин, полезна возможность генерации класса аксессора для указанного класса. Т.е. нужно сгенерировать совершенно отдельный набор классов не трогая исходный.


Впрочем, это можно спокойно и атрибутами сделать. Либо даже лучше отдельный класс. Или вот. Можно ли в макросе просканировать все типы, которые есть в компилируемой сборке?
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:34
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


IT>>>Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.


СТ>>Пока еще нет. Но я подкинул авторам такую идею. Работа уже идет.


IT>Вау, круто Может им ещё каких идей наподкидывать?

Ну, например? Чуваки вполне вменяемые.

IT>Например, для обхода торомозов рефлекшин, полезна возможность генерации класса аксессора для указанного класса. Т.е. нужно сгенерировать совершенно отдельный набор классов не трогая исходный.


И опять я тебя не понимаю.
Я за rfd давно не следил, поэтому от терминологии отвык.

Что касаемо тормозов рефлекшона (при генерации наследников), то эти затраты перенесутся на этап компиляции.
--
Re[7]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:38
Оценка:
Здравствуйте, IT, Вы писали:

IT>Вау, круто Может им ещё каких идей наподкидывать?


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

В генерации класса может участвовать несколько макросов. В том числе для генерации одного и того же метода. В качестве примера можно взять аспекты. Их можно применять пачками как к одному методу, так и к параметрам. При этом возможно, что между ними придётся каким-то образом передавать некоторые параметры. В принципе, одной глобальной внутренней хешь таблицы на все такие вещи было бы достаточно.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:42
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

IT>>Вау, круто Может им ещё каких идей наподкидывать?

СТ>Ну, например? Чуваки вполне вменяемые.

Это хорошо, у меня тут фантазия уже разыгралась
С ними как-то можно пообщаться?

IT>>Например, для обхода торомозов рефлекшин, полезна возможность генерации класса аксессора для указанного класса. Т.е. нужно сгенерировать совершенно отдельный набор классов не трогая исходный.


СТ>И опять я тебя не понимаю.


Я тоже пока не очень
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Я про то, чем ты сигнатуры получаешь? Через Reflection? Если так, то Nemerle -- это же .NET, там должно быть доступно все то, что доступно для C#. Может быть те же инструменты подойдут и для Nemerle?


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

Меня сейчас интересует где и когда будут вызываться макросы и как будут разруливаться конфликты.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:49
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, IT, Вы писали:


IT>>Например, для обхода торомозов рефлекшин, полезна возможность генерации класса аксессора для указанного класса. Т.е. нужно сгенерировать совершенно отдельный набор классов не трогая исходный.


IT>Впрочем, это можно спокойно и атрибутами сделать. Либо даже лучше отдельный класс. Или вот. Можно ли в макросе просканировать все типы, которые есть в компилируемой сборке?

Да, можно конечно. Над ними еще всякие штуки можно вытворять. Например, менять дерево наследования.
--
Re[9]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:54
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


IT>>>Вау, круто Может им ещё каких идей наподкидывать?

СТ>>Ну, например? Чуваки вполне вменяемые.

IT>Это хорошо, у меня тут фантазия уже разыгралась

IT>С ними как-то можно пообщаться?

Ага
--
Re[5]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:56
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, eao197, Вы писали:


E>>Я про то, чем ты сигнатуры получаешь? Через Reflection? Если так, то Nemerle -- это же .NET, там должно быть доступно все то, что доступно для C#. Может быть те же инструменты подойдут и для Nemerle?


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


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

Можно использовать компилятор, как библиотеку. В частности, один чувак делает автокомплишен по такой методике.

Насчет "где и когда" — есть три точки выполнения макросов. Процитирую,,,

5.1.1 Stages of class hierarchy building
Analysing object-oriented hierarchy and class members is a separate pass of
the compilation (as mentioned in section 3.1). First, it creates inheritance
relation between classes, so we know exactly all base types of given type.
After that, every member inside of them (methods, fields, etc.) is being
analysed and added to the hierarchy. After that also the rules regarding
implemented interface methods are checked.

For the needs of macros we have decided to distinguish three moments
in this pass at which they can operate on elements of class hierarchy. Every
macro can be annotated with a stage, at which it should be executed.

• BeforeInheritance stage is performed after parsing whole program and
scanning declared types, but before building sub-typing relation between them. It gives macro a freedom to change inheritance hierarchy
and operate on parse-tree of classes and members
• BeforeTypedMembers is when inheritance of types is already set. Macros can still operate on bare parse-trees, but utilize information about
subtyping.
• WithTypedMembers stage is after headers of methods, fields are already analysed and in bound state. Macros can easily traverse entire
class space and reflect type constructors of fields, method parameters,
etc. Original parse-trees are no longer available and signatures of class
members cannot be changed.
Later on we refer to these stages when describing class level macros.

--
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...
Пока на собственное сообщение не было ответов, его можно удалить.