Re[4]: Pretty print для variants
От: vaskir Россия vaskir.blogspot.com
Дата: 13.01.16 14:33
Оценка:
Всё исправил:

  Макрос
[MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = false, AllowMultiple = false)]
public macro Show(ty: TypeBuilder) {
    ShowImpl.Transform (ty)
}

module ShowImpl {
    GetRecordCtorParams(t: TypeBuilder): option[list[string]] {
        def recordCtor =
            t.GetConstructors(BindingFlags.Public)
             .FirstOrDefault(x => x.IsCustomAttributeDefined("Nemerle.Internal.RecordCtorAttribute"))
             .OfNull();
                    
        recordCtor.Map(x => {
            x.GetParameters()
             .Map(x => x.CustomAttributes)
             .Flatten()
             .Choose(x => 
                 match (x) {
                 | PExpr.Call(_, pars) =>
                     Some(pars.Choose(par =>
                         match (par) {
                         | PExpr.Literal(Literal.String(name)) => Some(name)
                         | _ => None()
                         }))
                 | _ => None()
                 })
             .Flatten()
          })
    }
    
    public Transform(ty: TypeBuilder): void {
        match (ty) {
        | _ when ty.IsDelegate with n = "delegates"
        | _ when ty.IsEnum with n = "enums"
        | _ when ty.IsInterface with n = "interfaces"
        | _ when ty.IsModule with n = "modules" => 
            Message.Error(ty.Location, $"The Show macro does not support $n."); 
        | _ => {
            ty.GetVariantOptions().Iter(opt => {
                unless (opt.GetDirectMembers().Any(x => x.Name == "ToString"))
                {
                    match (GetRecordCtorParams(opt)) {
                    | None => ()
                    | Some(pars) => {
                        def fieldsExpr: PExpr = pars.Fold(<[]>, (f, acc) => {
                             match (acc) {
                             | <[]> => <[ $(f: usesite).ToString() ]>
                             | _ => <[ $acc + ", " + $(f: usesite).ToString() ]>
                             }
                        });
                
                        def body: PExpr =
                            match (fieldsExpr) {
                            | <[]> => <[ $(opt.Name) ]>
                            | _ => <[ $(opt.Name) + "(" + $fieldsExpr + ")" ]>
                            };
                       
                        opt.Define(<[decl:
                            public override ToString(): string {
                                $body
                            }
                            ]>);
                      }
                   }
                }
            });
          }
        }
    }
}


Два вопроса:

Оптимально ли так определять наличие атрибута (по строковому имени)?
То же самое для определения, есть ли неунаследованный переопределенный ToString (opt.GetDirectMembers().Any(x => x.Name == "ToString"))
Получение имен аргументов через мэнчинг PExpr — ОК? Как-то низкоуровнево выглядит, учитываю, что у нас по идее уже есть типизированный метод.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.