Создание event-ов в макросах
От: hardcase Пират http://nemerle.org
Дата: 20.02.10 10:12
Оценка:
Ноги растут примерно отсюда
Автор: hardcase
Дата: 16.02.10
.

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

VD>По уму имя поля, add-ера и remove-ера нужно формировать динамически.

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

VD>Сплайс вида "$(name : usesite)" на этапе компиляции квази-цитаты преобразуется в:

VD>
VD>Splicable.Expression(<[ name : usesite ]>, GlobalEnv(...))
VD>

VD>Думается, что нужно разобрать выражение <[ name : usesite ]> "на запчасти" и вцепить из него выржение (в данном случае "name"), тип (в данном случае "usesite") и сформировать новые выражение которое будут выглядеть как-то так:
VD>
VD><[ ("_N_event_field_of_" + name) : usesite ]> // для имени поля 
VD><[ ("add_" + name) : usesite ]>               // для имени add-ера
VD><[ ("remove_" + name) : usesite ]>            // для имени remove-ра
VD>

VD>ну, и далее сформировать на их основе новые варианты Splicable.Expression() которые и использовать для формирования имен соответствующих сущностный.

Попробовал написать патч, решающий эту проблему. На "запчасти" разбираю сплайс-выражение таким вот образом:
AddSplicableNamePrefix(prefix : string, id : Splicable, loc : Location) : Splicable
{
  match(id)
  {
    | Splicable.Name(body) => MkSplicableName(prefix + body.Id, loc)
    | Splicable.HalfId(pref) => Splicable.HalfId(loc, Name(prefix + pref.Id, loc, pref.color, pref.context))
    | Splicable.Expression(expr) as id =>
      def new_expr = match(expr)
      {
        | PExpr.TypeEnforcement(expr, ty) =>
            <[ ($(prefix : string) + $expr.ToString()) : $ty ]>
        | _ =>   
            <[ ($(prefix : string) + $expr.ToString()) : usesite ]>
      }
      Splicable.Expression(loc, new_expr, id.env)
  }
}

Т.е. тупо преобразую исходное выражение в строку, и добавляю префикс ("add_", "remove_", или "_N_event_field_of_").

Соответственно переправил метод parse_event в файле MainParser.n (закомментированые строчки — оригинал):

parse_event (mutable loc : Location, mods : Modifiers,
             mutable customs : list [Token.SquareGroup]) : ClassMember
{
  def id = get_splicable_id ();
  def ret_type = parse_return_type(false, Location.Default);
  loc += ret_type.Location;
  //def plain_name = id.ToString();
  def val_n = MkSplicableName("value", loc.AsGenerated());
  mutable remove = null;
  mutable add = null;
  
  match (peek_token ())
  {
    | Token.BracesGroup as group =>
      shift ();
      take_attributes_out (ref customs, System.AttributeTargets.Event, true, mods);

      foreach (Token.LooseGroup (toks) in group)
      {
        push_stream (toks);
        mutable mycustoms = get_customs ();
        def mymods = get_modifiers ();

        match (get_token ())
        {
          | Token.Identifier (i) as nametok =>
            def genLoc = nametok.Location.AsGenerated();
            match (i)
            {
              | "remove" | "add" =>
                if (i == "remove")
                  unless (remove == null)
                    Message.Error (nametok.Location, "event cannot have multiple remove methods");
                else
                  unless (add == null)
                    Message.Error (nametok.Location, "event cannot have multiple add methods");

                def method_atts = Modifiers (mymods, []);                           
                take_attributes_out (ref mycustoms, System.AttributeTargets.Method, false, method_atts);
                parse_top_extensions (method_atts, MacroTargets.Method);                    

                def par_atts = Modifiers (NemerleAttributes.None, []);
                take_attributes_out (ref mycustoms, System.AttributeTargets.Parameter, true, par_atts);
                def method_parms = [PParameter (val_n, ret_type, par_atts)];
                
                def bodyBracesGroup = TryPeekBracesGroup();
                def (kind, body) = parse_accessor_body (method_parms, []);
                //def name = MkSplicableName(i + "_" + plain_name, plain_name, genLoc);
                def name = AddSplicableNamePrefix(i + "_", id, genLoc);
                def fh = PFunHeader (genLoc, name, PExpr.Void (), method_parms);
                def method = ClassMember.Function (CombineLocations(nametok, bodyBracesGroup), 
                               name, method_atts, fh, kind, body);
                method.ParsedBody = body;
                InitBodyLocations(method, bodyBracesGroup);
                method._env = env;
                if (i == "remove")
                  remove = method;
                else
                  add = method;

              | _ =>
                def fieldName = MkSplicableName(i, nametok.Location);
                def ty = parse_return_type(false, Location.Default);
                def attrs = Modifiers (mymods, []);
                take_attributes_out (ref mycustoms, System.AttributeTargets.Field, true, attrs);
                def f = ClassMember.Field (nametok.Location.Combine(ty.Location), fieldName, attrs, ty);
                def embed = PExpr.Quoted (genLoc, SyntaxElement.ClassMember (f));
                mods.AddCustomAttribute(<[ $(MkNameGenerated("Nemerle") : name).InternalMacros.EventEmbeddedField($embed) ]>);
            }

          | x => Error (x, "expecting event accessor"); 
        }
        pop_stream ("property member");
      }
      when (add == null || remove == null)
        Message.Error (loc, "both of event accessors `add' and 'remove' must be specified");
      
      ClassMember.Event (loc + group.Location, id, mods, ret_type, null, add, remove)
      
    | Token.EndOfGroup =>
      // first take out event attributes (those without target also get here)
      take_attributes_out (ref customs, System.AttributeTargets.Event, false, mods);
      
      /// auto-generated field
      //def fieldName = "_N_event_field_of_" + plain_name;
      def fmods = NemerleAttributes.Private %| NemerleAttributes.Mutable;
      //def doRenaming = !Manager.IsIntelliSenseMode;
      def generatedLoc = loc.AsGenerated();
      //def field_name = if (doRenaming) MkTempName(fieldName, generatedLoc) else MkName(fieldName, generatedLoc);
      def field_name = AddSplicableNamePrefix("_N_event_field_of_", id, generatedLoc);
      def field_attrs = Modifiers (fmods, []);
      take_attributes_out (ref customs, System.AttributeTargets.Field, false, field_attrs);
      //def field = ClassMember.Field (generatedLoc, Splicable.Name (field_name), field_attrs, ret_type);
      def field = ClassMember.Field (generatedLoc, field_name, field_attrs, ret_type);
      field.ParsedType = ret_type;

      def method_atts = Modifiers (mods.Attributes, []);                                         
      take_attributes_out (ref customs, System.AttributeTargets.Method, true, method_atts);

      def method_parms = [PParameter (val_n, ret_type, Modifiers ())];

      //def name = MkSplicableName("add_" + plain_name, generatedLoc);
      def name = AddSplicableNamePrefix("add_", id, generatedLoc);
      def fh = PFunHeader (generatedLoc, name, PExpr.Void (generatedLoc), method_parms);
      // funbody is filled during typing
      add = ClassMember.Function (generatedLoc, name, method_atts, fh, [], null);
      add._env = env;

      //def name = MkSplicableName("remove_" + plain_name, generatedLoc);
      def name = AddSplicableNamePrefix("remove_", id, generatedLoc);
      def fh = PFunHeader (generatedLoc, name, PExpr.Void (generatedLoc), method_parms);
      remove = ClassMember.Function (generatedLoc, name, method_atts, fh, [], null);
      remove._env = env;

      ClassMember.Event (loc, id, mods, ret_type, field, add, remove)
      
    | t => Error (t, "expecting `;' or `{ }' in event declaration"); null
  }
}


Сей грубый способ формирования имен методов и поля в принципе работает, для макроса вида
using Nemerle;
using Nemerle.Compiler;
using PT = Nemerle.Compiler.Parsetree;

namespace Lib {

    [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Class)]
    public macro CreateEvents(tb : TypeBuilder) {
        Helper.CreateEvents(tb, [ "Foo", "Bar" ]);
    }

    module Helper {
        public CreateEvents(tb :TypeBuilder, events : list[string]) : void {
            foreach(name in events) {
                tb.Define(<[ decl:
                    public event $(name : usesite) : System.EventHandler;
                ]>);
            }
        }
    }
}


Рефлектор показал (id — это текущий name в foreach-е):

       tb.Define(
new ClassMember.Event(
    new Splicable.Name(new Name(id, ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
    
    new Modifiers(NemerleAttributes.Public, list<PExpr>.Nil._N_constant_object),
    
    new PExpr.Member(
        new PExpr.Ref(Name.NameInCurrentColor("System", _N_MacroContexts.Get(1, ManagerClass.Instance))),
        new Splicable.Name(Name.NameInCurrentColor("EventHandler", _N_MacroContexts.Get(1, ManagerClass.Instance)))),
    
    new ClassMember.Field(
        new Splicable.Name(new Name("_N_event_field_of_" + id.ToString(), ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
        new Modifiers(NemerleAttributes.Mutable | NemerleAttributes.Private, list<PExpr>.Nil._N_constant_object),
        new PExpr.Member(
            new PExpr.Ref(Name.NameInCurrentColor("System", _N_MacroContexts.Get(1, ManagerClass.Instance))),
            new Splicable.Name(Name.NameInCurrentColor("EventHandler", _N_MacroContexts.Get(1, ManagerClass.Instance))))),
    
    new ClassMember.Function(
        new Splicable.Name(new Name("add_" + id.ToString(), ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
        new Modifiers(NemerleAttributes.Public, list<PExpr>.Nil._N_constant_object),
        new PFunHeader(
            new Typarms(list<Splicable>.Nil._N_constant_object, list<Constraint>.Nil._N_constant_object),
            new Splicable.Name(new Name("add_" + id.ToString(), ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
            new PExpr.Void(),
            new list<PParameter>.Cons(
                new PParameter(
                    new Splicable.Name(Name.NameInCurrentColor("value", _N_MacroContexts.Get(1, ManagerClass.Instance))),
                    new Modifiers(NemerleAttributes.None, list<PExpr>.Nil._N_constant_object),
                    new PExpr.Member(
                        new PExpr.Ref(Name.NameInCurrentColor("System", _N_MacroContexts.Get(1, ManagerClass.Instance))),
                        new Splicable.Name(Name.NameInCurrentColor("EventHandler", _N_MacroContexts.Get(1, ManagerClass.Instance))))),
                list<PParameter>.Nil._N_constant_object)),
        list<PExpr>.Nil._N_constant_object,
        FunBody.Abstract._N_constant_object),
            
    new ClassMember.Function(
        new Splicable.Name(new Name("remove_" + id.ToString(), ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
        new Modifiers(NemerleAttributes.Public, list<PExpr>.Nil._N_constant_object),
        new PFunHeader(
            new Typarms(list<Splicable>.Nil._N_constant_object, list<Constraint>.Nil._N_constant_object),
            new Splicable.Name(new Name("remove_" + id.ToString(), ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
            new PExpr.Void(),
            new list<PParameter>.Cons(
                new PParameter(
                    new Splicable.Name(Name.NameInCurrentColor("value", _N_MacroContexts.Get(1, ManagerClass.Instance))),
                    new Modifiers(NemerleAttributes.None, list<PExpr>.Nil._N_constant_object),
                    new PExpr.Member(
                        new PExpr.Ref(Name.NameInCurrentColor("System", _N_MacroContexts.Get(1, ManagerClass.Instance))),
                        new Splicable.Name(Name.NameInCurrentColor("EventHandler", _N_MacroContexts.Get(1, ManagerClass.Instance))))),
                list<PParameter>.Nil._N_constant_object)),
        list<PExpr>.Nil._N_constant_object,
        FunBody.Abstract._N_constant_object)));


Т.е. вроде бы то что нужно... проблема возникает при компиляции.
Для построенных таким макросом событий
[CreateEvents] class C1 { }

компилятор 4 раза жалуется на value (видимо для каждого аксессора для двух эвентов Foo и Bar):
event-macro.n:3:2:3:14: ←[01;31merror←[0m: unbound name `value'
event-macro.n:3:2:3:14: ←[01;31merror←[0m: unbound name `value'
event-macro.n:3:2:3:14: ←[01;31merror←[0m: unbound name `value'
event-macro.n:3:2:3:14: ←[01;31merror←[0m: unbound name `value'

В остальном все тесты компилятора проходят.

События с фиксированным именем, вида
tb.Define(<[ decl:
    public event OnDoSomething : System.EventHandler;
]>);

создаются нормально.
http://nemerle.org/Banners/?t=Developer!&g=dark /* иЗвиНите зА неРовнЫй поЧерК */
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.