Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

[Nemerle] проблема с типами при написании макроса

4.0K
01 августа 2010 года
ApokALEXIS
112 / / 23.08.2006
макрос надстраивается над пропортой
 
Код:
[Children("ParentID")]
        public  ChildrenProp : List[Child]
        {
            get
            {
            }
        }


проблема -
1) надо из property.returnType достать его Generic параметр (в данном случае Child из List[Child])
2) чтобы в классе обьявить метод (.Define(<[...]>)) необходимо иметь TypeBuilder этого класса. (там коментарием псевдокодом написано что я хочу получить)

Код:
[Nemerle.MacroUsage (MacroPhase.BeforeInheritance,
                       MacroTargets.Property)]
  macro Children(t : TypeBuilder, p : ParsedProperty, qstring : string)
  {
        def property = p : ClassMember.Property;
        def cached_instance = Macros.NewSymbol ("cached");
        def GetFor = Macros.NewSymbol ("GetFor" + $"$(t.ParsedTypeName)");
       
        t.Define(<[ decl: private static mutable $(cached_instance: name): $(t.ParsedTypeName) = null;]>);    // кешируем полученное значение

// в классе ретурн типа пропорты обьявляем функцию GetFor.
        (property.returnType./*GetGenericTypes[0].Typebuilder*/).Define(<[ decl: public static $(GetFor : name)(ID : Guid) : $(property.returnType)
                                {                                          
                                    GetByQuery($"$($qstring) = '$(ID)'");
                                }]>);
                             

      def accessor(accessor)
      {
        | Some(fn) => fn
        | _ => Message.Error(property.Location, "Both getter and setter should be specified for dependency property."); null
      };
      def getter = accessor(property.getter);
      getter.Body = <[  // засетиваем геттер нашей пропорты
                        if( $(cached_instance: name) != null)
                            $(cached_instance: name)
                        else
                            $(cached_instance: name) = ($(property.returnType))/*тож самое - нужный нам генерик тип сюда*/.$(GetFor : name)(ID);
                        ]>;      
  }
5
01 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: ApokALEXIS

1) надо из property.returnType достать его Generic параметр (в данном случае Child из List[Child])
2) чтобы в классе обьявить метод (.Define(<[...]>)) необходимо иметь TypeBuilder этого класса. (там коментарием псевдокодом написано что я хочу получить)


tb - билдер типа, в котором объявлено свойство pb.
Первый матч выцепляет единственный артумент типа
BindFixedType позволяет по PExpr получить FixedType (или не получить. если программист допутсил ошибку), из которого можно вытащить билдер.

Код:
public DoWork(tb : TypeBuilder, pb : PT.ClassMember.Property) : void {
            def ty_arg = match(pb.returnType) {
              | <[ $_[$ty_arg] ]> => ty_arg;
              | _ => Message.FatalError("Improper usage of macro");
            }
            def ft = tb.BindFixedType(ty_arg);
            match(ft?.TypeInfo) {
              | child_tb is TypeBuilder => Message.Hint(child_tb.ToString());
              | _ => Message.Hint("Type builder not found")
            }
        }
4.0K
02 августа 2010 года
ApokALEXIS
112 / / 23.08.2006
тестировал Message'ами

def ft = tb.BindFixedType(ty_arg);

tb, ty_arg не нулы, указывают на типы, даже с указанием неймспейса
не могу понять откуда возник иксепшн
 
Код:
Error   22  Exception System.NullReferenceException: В экземпляре объекта не задана ссылка на объект.
   в Nemerle.Compiler.TypeBuilder.BindFixedType(PExpr t)

а вообще круто! до таких вещей сам не допрешь. изучаю язык в основном читая его исходники
5
02 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: ApokALEXIS
тестировал Message'ами


Для отладки макроса можно использовать инструкцию:

 
Код:
unless(Manager().IsIntelliSenseMode)
    _ = System.Diagnostics.Debugger.Launch(); // или assert2(false);

Она запустит отладчик прямо во время компиляции проекта.


Цитата: ApokALEXIS

def ft = tb.BindFixedType(ty_arg);

tb, ty_arg не нулы, указывают на типы, даже с указанием неймспейса
не могу понять откуда возник иксепшн
 
Код:
Error   22  Exception System.NullReferenceException: В экземпляре объекта не задана ссылка на объект.
   в Nemerle.Compiler.TypeBuilder.BindFixedType(PExpr t)



Нда, писал второпях. К сожалению забыл я сказать, что у BindFixedType на стадии BeforeInheritance какие-то проблемы, макрос нужно сдвигать на стадию BeforeTypedMembers.

Весь код макроса:

Код:
using System;
using SCG = System.Collections.Generic;

using Nemerle;
using Nemerle.Assertions;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Utility;
using PT = Nemerle.Compiler.Parsetree;
using TT = Nemerle.Compiler.Typedtree;

namespace ChildrenMacro {

  [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Property)]
  macro Children(tb : TypeBuilder, pb : ParsedProperty, q : string) {
    ChildrenImpl.DoWork(tb, pb, q);
  }

  module ChildrenImpl {

    GetChildTypeBuilder(tb : TypeBuilder, pb : PT.ClassMember.Property) : option[TypeBuilder] {
      //assert2(false);  // позволяет отлаживаться
      def ty_arg = match(pb.returnType) {
        | <[ $_[$ty_arg] ]> => ty_arg;
        | _ => Message.FatalError("Type with single type argument expected.");
      }
     
      def ft = tb.BindFixedType(ty_arg);
      match(ft?.TypeInfo) {
        | child_tb is TypeBuilder => Some(child_tb)
        | _ => None()
      }
    }
   
    public DoWork(tb : TypeBuilder, pb : PT.ClassMember.Property, q : string) : void {
      match(GetChildTypeBuilder(tb, pb)) {
        | Some(child_tb) =>
          def cached_instance = Macros.NewSymbol("cached");
          tb.Define(<[ decl:
            private static mutable $(cached_instance : name) : $(pb.returnType) = null;
          ]>);
          def get_for = Macros.NewSymbol("GetFor");
          child_tb.Define(<[ decl:
            internal static $(get_for : name) (id : System.Guid) : $(pb.returnType) {
              def q = $(q : string);
              GetByQuery($"$q = '$id'");
            }
          ]>);

          def get_accessor(_) {
            | Some(f) => f;
            | _ => Message.FatalError(pb.Location, "Both getter and setter should be specified for dependency property.");
          }
          def getter = get_accessor(pb.getter);
          getter.Body = <[
            when(null == $(cached_instance : name))
              $(cached_instance : name) = $(child_tb.ParsedName : name).$(get_for : name)(ID);
            $(cached_instance : name)
          ]>;

        | _ => Message.FatalError("Type must be declared in this project.");
      }
    }
  }
}



Цитата: ApokALEXIS
а вообще круто! до таких вещей сам не допрешь. изучаю язык в основном читая его исходники


Это пожалуй главная проблема - крайне мало документации. :(

4.0K
04 августа 2010 года
ApokALEXIS
112 / / 23.08.2006
макрос вешается на пропорту

[TransitLink2("TableName", "ParentID", "ChildID")]
public Children:MultyLink[Child]
{get{null}}

используется при связи много ко много - требует в БД таблицы развязки, и соотв класса

в данном макросе создаю этот класс-связку и потом к нему обращаюсь, прописываю геттер пропорты, методы обьявляю в тек классе итд, неважно

в строке curenv.Define(LinkClass); вылетает иксепшн "Internal compiler error 'env is null for Data.NewEngine.TableName__", в дебаге LinkClass.Env равен GlobalEnv (собсно тому, что засетил)

идеи как создать в макросе класс передирал с Nemerle.Utility.Accessor

Код:
using System;
using SCG = System.Collections.Generic;

using Nemerle;
using Nemerle.Assertions;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Utility;
using System.Linq;
using PT = Nemerle.Compiler.Parsetree;
using TT = Nemerle.Compiler.Typedtree;

namespace DbEngine.Macroses
{

    [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Property)]
    macro TransitLink2(tb : TypeBuilder, pb : ParsedProperty, table : string, idMy : string, idChild : string)
    {
        unless(tb.GlobalEnv.Manager.IsIntelliSenseMode)
            TransitLink2Impl.DoWork(tb, pb, table, idMy, idChild)
    }

  module TransitLink2Impl
  {

    public DoWork(tb : TypeBuilder, pb : PT.ClassMember.Property, table : string, idMy : string, idChild : string) : void
    {
        def GetLinkTB()
        {
            def firstName = ...;
            def secondName= ...;
            def parentIdNo = ...;
            def linkClassName = Compiler.Parsetree.Name($"$(table)__");

            def curenv = tb.GlobalEnv;
            match(curenv.BindFixedType(<[$(linkClassName : name)]>)?.TypeInfo) // check if already exists
              {
                | child_tb is TypeBuilder => (parentIdNo, Some(child_tb))
                | _=>
     
                def LinkClass =
                      <[ decl:
                          [Table($"$(table : string)")]
                          internal class $(linkClassName : name) : BaseClass[$(linkClassName : name)], ILink
                          {
                              public this(ID1 : Guid, ID2 : Guid)
                              {
                                    base();    
                                    reader[$"$(firstName : string)"]  = ID1;
                                    reader[$"$(secondName : string)"] = ID2;
                              }
                             
                              [ParentLink($"$(firstName : string)")]  // свой другой макрос
                              public FirstClass : BaseClass {get{null}}
                             
                              [ParentLink($"$(secondName : string)")]
                              public SecondClass : BaseClass {get{null}}
                          }
                        ]>;
                 
                  LinkClass.SetEnv(curenv);
                  def cc = curenv.Define(LinkClass);
                  cc.Compile();
                  (parentIdNo, Some(cc))
              }
      }
     
        match(GetLinkTB())
        {
          | (parentIdNo, Some(link_tb)) =>  
       
          ...
       
        | _ => Message.FatalError("Error generating Link Class");
      }
        | _ => Message.FatalError("Type must be declared in this project.");
      }
    }
  }
}
5
04 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: ApokALEXIS

в строке curenv.Define(LinkClass); вылетает иксепшн "Internal compiler error 'env is null for Data.NewEngine.TableName__"

В цитатах объявляющих класс/структуру можно объявлять только конструкторы:

 
Код:
<[ decl:
  class X {
     public this() { doSomth; }
  }
]>

Остальные члены нужно объявлять либо отдельными вызовами Define
 
Код:
def tb = p.Define(<[ decl:
 class MyClass { }
]>);
tb.Define(<[ decl : public Foo() : void { blabla } ]>);
tb.Define(<[ decl : public Bar() : void { blabla } ]>);


либо такой штукой:
 
Код:
def members = [
  <[ decl : public Foo() : void { blabla } ]>,
  <[ decl : public Bar() : void { blabla } ]>
];
def tb = p.Define(<[ decl:
 class MyClass { ..$members }
]>);


Да, это неудобно сам нарывался, но еще ни у кого руки не дошли исправить :(
4.0K
04 августа 2010 года
ApokALEXIS
112 / / 23.08.2006
 
Код:
def LinkClass =
                      <[ decl:
                          internal class $(linkClassName : name) : BaseClass[$(linkClassName : name)], ILink
                          {

                          }
                        ]>;

пустой класс. но ему все равно не нравится.
в качестве env беру GlobalEnv билдера, в котором нахожусь
5
04 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: ApokALEXIS
 
Код:
def LinkClass =
                      <[ decl:
                          internal class $(linkClassName : name) : BaseClass[$(linkClassName : name)], ILink
                          {

                          }
                        ]>;

пустой класс. но ему все равно не нравится.
в качестве env беру GlobalEnv билдера, в котором нахожусь



А для чего делается вот это?

 
Код:
LinkClass.SetEnv(curenv);

Класс и так будет объявлен в пространстве имен curenv.
4.0K
04 августа 2010 года
ApokALEXIS
112 / / 23.08.2006
попытка засетить енвайронмент) в Accessor так было.. сам удивлялся.
но оно что с ним что без него не пашет
может еще дело в стадии компиляции макроса?
[MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Property)]
5
04 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: ApokALEXIS
попытка засетить енвайронмент) в Accessor так было.. сам удивлялся.
но оно что с ним что без него не пашет
может еще дело в стадии компиляции макроса?
[MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Property)]


Хмм не знаю, трудно смоделировать.
BindFixedType кстати тут совсем не нужно использовать. BindFixedType генерирует ошибку компиляции, если не смог найти тип, потому нужно использовать LookupType:

Код:
[MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Property)]
    macro TransitLink2(tb : TypeBuilder, pb : ParsedProperty, table : string) {
      unless(tb.GlobalEnv.Manager.IsIntelliSenseMode)
        TransitLink2Impl.DoWork(tb, pb, table);
    }

    module TransitLink2Impl {

        public DoWork(tb : TypeBuilder, pb : PT.ClassMember.Property, table : string) : void {
            def linkClassName = "Blabla_" + table;
            match(tb.GlobalEnv.LookupType([linkClassName])) // check if already exists
            {
                | Some(_ is TypeBuilder) => () // уже объявлен в этой сборке
                | Some(_ is TypeInfo) => () // объявлен в другой сборке
                | _ => // еще не объявлен
                def LinkClass = <[ decl:
                  public class $(linkClassName : usesite) { }
                ]>;
                def cc = tb.GlobalEnv.Define(LinkClass);
                cc.Compile();
            }
        }

    }




З.Ы. Использую компилятор версии r9037.
4.0K
04 августа 2010 года
ApokALEXIS
112 / / 23.08.2006
спасибо, сам пока эту проблему не решил закоментил match

таки я не могу в это поверить, но проблема была в
$(linkClassName : usesite) (я же использовал Нейм)
а какая разница вообще?
5
04 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: ApokALEXIS
таки я не могу в это поверить, но проблема была в
$(linkClassName : usesite) (я же использовал Нейм)
а какая разница вообще?


PT.Name захватывает (или не захватывает, как в вашем случае) GlobalEnv в котором объявлен (поле context). Я не рекомендую изготавливать Name вручную - можно нарваться на самые веселые грабли:

 
Код:
def linkClassName = Compiler.Parsetree.Name($"$(table)__");
5
07 августа 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: hardcase
В цитатах объявляющих класс/структуру можно объявлять только конструкторы:
 
Код:
<[ decl:
  class X {
     public this() { doSomth; }
  }
]>

Остальные члены нужно объявлять либо отдельными вызовами Define
 
Код:
def tb = p.Define(<[ decl:
 class MyClass { }
]>);
tb.Define(<[ decl : public Foo() : void { blabla } ]>);
tb.Define(<[ decl : public Bar() : void { blabla } ]>);


либо такой штукой:
 
Код:
def members = [
  <[ decl : public Foo() : void { blabla } ]>,
  <[ decl : public Bar() : void { blabla } ]>
];
def tb = p.Define(<[ decl:
 class MyClass { ..$members }
]>);


Да, это неудобно сам нарывался, но еще ни у кого руки не дошли исправить :(




Исправил в r9043. Теперь члены классов можно объявлять в квазицитате с этим классом.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог