Re: peephole macros
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.09.11 22:44
Оценка:
Здравствуйте, xeno.by, Вы писали:

XB>Итак, к делу. Дополнительно к явному вызову макросов (функционально или атрибутами), возможно, имеет смысл реализовать автоматическую активацию макросов по шаблону. Скажем, встретился в коде вызов <[ $(foo: Foo).Bar(..$args) ]> — вызываем для него макрос, который, возможно, решит немножко подпилить или вообще заменить этот вызов. Некоторым образом, это напоминает лисповский defadvice, но мы не обязаны ограничиваться только подпиливанием определенных методов. С помощью этой фичи можно было бы реализовать "честную" поддержку LINQ — обращения к методам IQueryable автоматически бы вызывали преобразование выражений Немерле в выражения сишарпа.


Все сильно сложнее чем ты себе можешь представить. Для замены кода вроде той что делается для поддержки преобразования в деревья выражений мало найти синтаксическую конструкцию. Для этого нужно вмешиваться в процесс типизации.

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

XB>Какие здесь могут быть проблемы? Будет ли это практично в реализации?


Да море. Проще разобраться как оно реально работает, чем объяснять.
Если в двух словах, то конструкция $(foo: Foo).Bar очень перегружена. И без специального кода процесс резрешения перегрузки просто провалится, так как на входе мы имеем ссылку на функцию (с не выведенными типами), а на выходе нам нужно получить дерево выражений. И еще нужно догадаться, что такое преобразование требуется.
def (delegate_tc, exprTree_tc, argsTys, ret) =
  if (IsFunctional(expr))
  {
    def (del, exprTree, args, ret) = TryExtractFunType(target.Hint);

    if (args != null && ret != null)
    {
      def fnTy = FixedType.Fun(TypeVar.FromList(args), args.Length, ret);
      def ok = expr.Type.TryRequire(fnTy);

      if (ok)
      {
        _ = expr.Type.Require(fnTy);
        (del, exprTree, args, ret)
      }
      else (null, null, null, null)
    }
    else (del, exprTree, args, ret)
  }
  else (null, null, null, null);

if (exprTree_tc != null && !SkipExpressionTreeConvertion)
{
  match (expr)
  {
    | TExpr.MacroEnvelope(_, _, TExpr.DefFunctionsIn([fn], _), _)
    | TExpr.DefFunctionsIn([fn], _) =>
      def <[ fundecl: $_name[ ..$typarms1] (..$args1) : $ty1 where ..$tyconstrs1 $body1 ]> = fn.Parsed;
      def trySetType(parm, ty)
      {
        when (parm.Type is PExpr.Wildcard)
          parm.Type = PExpr.TypedType(ty)
      }

      mutable retTy = ty1;

      when (!args1.IsEmpty && argsTys.Length == args1.Length)
      {
        NList.Iter2(args1, argsTys, trySetType);

        when (retTy is PExpr.Wildcard)
          retTy = PExpr.TypedType(ret);
      }

      def lamda = <[ fun [ ..$typarms1] (..$args1) : $retTy where ..$tyconstrs1 $body1 ]>;

      def expr2 = TryTyping(() =>
        TypeExpr(<[ Nemerle.Linq.ToExpression($lamda) ]>));

      if (IsError (expr2) || !expr2.Type.TryRequire(target))
        null
      else
      {
        // The source texpr can be not typed. Help type it...
        def fnTy  = FixedType.Fun(TypeVar.FromList(argsTys), argsTys.Length, ret);
        def ok    = fn.decl.Type.Require(fnTy);

        expr2.Type.ForceRequire(target);
        _ = ok;
        expr2
      }

    | _ => null
  }
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.