How to get what a PExpr.Ref represents: local value, parameter, etc...

36 views
Skip to first unread message

Liviu

unread,
Aug 19, 2011, 4:33:47 PM8/19/11
to Nemerle Forum
Hi,

I try to create a macro that applied to a type will read the AST to
search for some code patterns:

foreach (member in typeBuilder.Ast.GetMembers())
{
match(member)
{
| Function as f =>

def walker = ExprWalker();
def info = MyWalkerInfo(typer, f);
walker.Walk(f.Body,info.MyExprWalkHandler);
| _ => ();
}
}


What i don't know:

1) When the calllback received a PExpr.Ref i don't know how to get
what it represents: local value, parameter, member, operator.
I noticed a property: TypedObject but is always null.

2) Can I modify the AST? it is WithTypedMembers stage.

Thanx


VladD2

unread,
Aug 19, 2011, 6:00:26 PM8/19/11
to nemer...@googlegroups.com
2011/8/20 Liviu <liv...@gmail.com>

What i don't know:
 1)  When the calllback received a PExpr.Ref i don't know how to get
what it represents: local value, parameter, member, operator.
    I noticed a property: TypedObject but is always null.

The TypedObject property set after typing is finished. A macroattribute callled before typing member bodies.

The WithTypedMembers phase mean - when members parameters and return value is bind with real type.

If you need type information, you should type expressions manually.

What problem you try to solve?
 
2)  Can I modify the AST? it is WithTypedMembers stage.

You can modify methods bodies on thish phase.

Liviu

unread,
Aug 20, 2011, 3:18:32 AM8/20/11
to Nemerle Forum

I want to get dependencies between the passes parameter and other
elements inside the body of the method.
The methods are some rules:

void Rule1( Target t)
{
t.DisplayName = t.No + t.Description;
}

I want to analyze the body of the method and get the dependency:
t.DisplayName is dependent upon t.No and t.Description.
But i need to know when walking the AST that

Ref : t is actually the parameter t.

I noticed that even the operator + is a Ref +.
I want to know more info about the elements in the AST but i don't get
how...

On Aug 20, 1:00 am, VladD2 <v...@rsdn.ru> wrote:
> 2011/8/20 Liviu <livi...@gmail.com>

VladD2

unread,
Aug 20, 2011, 2:14:23 PM8/20/11
to nemer...@googlegroups.com
суббота, 20 августа 2011 г. 11:18:32 UTC+4 пользователь Liviu написал:

I want to get dependencies between the passes parameter and other
elements inside the body of the method.

It is a solution, but not  description of problem ;).
 
The methods are some rules:

void Rule1( Target t)
{
   t.DisplayName = t.No  + t.Description;
}

I want to analyze the body of the method and get the dependency: 
t.DisplayName is dependent upon t.No and t.Description.
But i need to know when walking the AST that

As I say above, if you need type information, you should type expressions manually. It's not simple subject, sorry.

The example below show how type expression manually, how wait while compiler infer all types in subexpressions and how to analyse typed expressions.
I divide macro to two macros.
The first (CalcDependencies) is macroattrabute which wrap the method body in second macro (DoCalcDependencies).
The second macro do all other staff.

CalcDependencies.n
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Compiler.Typedtree;

namespace MacroLibrary
{
  [MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Method)]
  macro CalcDependencies(typeBuilder : TypeBuilder, method : MethodBuilder)
  {
    def originalLocation = method.Body.Location;
    method.Body = <[ DoCalcDependencies($(method.Body)) ]>; // wrap the method body in DoCalcDependencies macro
    method.Body.Location = originalLocation;
  }
}
DoCalcDependencies.n
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Compiler.Typedtree;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;


namespace MacroLibrary
{
  macro DoCalcDependencies(code : PExpr)
  {
    DoCalcDependenciesImpl.DoTransform(Macros.ImplicitCTX(), code)
  }
  
  module DoCalcDependenciesImpl
  {
    public DoTransform(typer : Typer, code : PExpr) : PExpr
    {
      Macros.DefineCTX(typer);
      
      def analyse(_code, typed)
      {
        def parms = typer.CurrentMethodBuilder.Header.parms;
        def isParametr(x : LocalValue) { parms.Exists(p => p.decl.Equals(x)) }

        def wolk(info : ExprWalkInfo) : void // Walk through typed body representation (typed expression)
        {
          match (info.Node)
          { // typed representation of <[ $x.$y = $e2 ]>
            | TExpr.Assign(TExpr.PropertyMember(TExpr.LocalRef(local1), prop) as e1, e2) when isParametr(local1) =>
              //assert2(false); // use it if you need to debug this code

              // collect dependences...
              def dependOn = List();
              def collectDependences(info : ExprWalkInfo) : void
              {
                match (info.Node)
                {
                  // match member access on same local variable (i.e. on one of parameters)
                  | TExpr.MethodRef     (TExpr.LocalRef(local2), member, _, _) as e when local2.Equals(local1) 
                  | TExpr.FieldMember   (TExpr.LocalRef(local2), member)       as e when local2.Equals(local1) 
                  | TExpr.PropertyMember(TExpr.LocalRef(local2), member)       as e when local2.Equals(local1) =>
                    dependOn.Add((e, member));
                    
                  | _ => ()
                }
              }
              def wolker = ExprWalker();
              wolker.Walk(e2, collectDependences);
              
              // Print collected dependences as compiler hints
              if (dependOn.IsEmpty())
                Message.Hint(e1.Location, $"The $(local1.Name).$(prop.Name) has no dependences.");
              else
              {
                Message.Hint(e1.Location, $"The $(local1.Name).$(prop.Name) dependent on:");
                foreach ((expr, member) in dependOn)
                  Message.Hint(expr.Location, member.ToString());
              }
              
            | _ => ()
          }
        }
        
        def wolker = ExprWalker();
        wolker.Walk(typed, wolk); // Walk through typed body representation (typed expression)
        
        <[ $(typed : typed) ]> // Returning of the typed expressin prevent us from retyping, but we can return "code".
      }

      def typed = typer.TypeExpr(code); // type the expression manually
      // Now expression is typed, but it can has delayed typing actions (not inferred yet subexpressions).
      // We should wait while compiler infer all types in subexpressions.
      typer.TransformWhenAllTypesWouldBeInfered(analyse, typed, code); // wait for type enferrence is finished and call 'analyse'
    }
  }
}
Using macros...
Main.n
using MacroLibrary;

[Record]
class Target
{
  public DisplayName : string { get; set; }
  public No          : string { get; set; }
  public Description : string { get; set; }
}

module Program
{
  [CalcDependencies]
  Rule1(t : Target) : void
  {
    t.DisplayName = t.No  + t.Description;
  }

  Main() : void
  {
    
  }
}
Compiler output:
Main.n(25,5): warning : hint: The t.DisplayName dependent on:
Main.n(25,21): warning : hint: property: Target.No : string { get; set; }
Main.n(25,29): warning : hint: property: Target.Description : string { get; set; }

Liviu U

unread,
Aug 20, 2011, 5:05:09 PM8/20/11
to nemer...@googlegroups.com
Vlad, thank you for the detailed example, it is a very good start point for me.

From the example i understand that:

1)  At WithTypedMemberrs stage we have an AST but there is no type info yet for the statements inside method body. I noticed that function header parameters have a type.
2)  You wrapped the body of the method in a macro.
3)  When macro runs, there is type information available.

Thanx again

2011/8/20 VladD2 <v...@rsdn.ru>

--
nemerle-en Google Group, try http://groups.google.com/group/nemerle-en
for more options.

VladD2

unread,
Aug 20, 2011, 5:37:07 PM8/20/11
to nemer...@googlegroups.com


21 августа 2011 г. 1:05 пользователь Liviu U <liv...@gmail.com> написал:

1)  At WithTypedMemberrs stage we have an AST but there is no type info yet for the statements inside method body. I noticed that function header parameters have a type.

Yes.
 
2)  You wrapped the body of the method in a macro.

Yes.
 
3)  When macro runs, there is type information available.

Not really. A expression level macro expand during typeng. The compiler run a macro, get as result the converted expression and type resulted expression.
Then, if you need typed expressoion (type info) you should type expresson himself.

The typer.TypeExpr() - type given expression.
The typer.TransformWhenAllTypesWouldBeInfered() help you wait when all types will be inferred.

Liviu U

unread,
Aug 20, 2011, 6:57:35 PM8/20/11
to nemer...@googlegroups.com
Ok. Thank you.

Interesting thing.

But after we run  typer.TransformWhenAllTypesWouldBeInfered(), would still be possible to change the AST?





2011/8/21 VladD2 <v...@rsdn.ru>

--

VladD2

unread,
Aug 20, 2011, 8:21:30 PM8/20/11
to nemer...@googlegroups.com
21 августа 2011 г. 2:57 пользователь Liviu U <liv...@gmail.com> написал:

But after we run  typer.TransformWhenAllTypesWouldBeInfered(), would still be possible to change the AST?

Yes. The 'analyse' function (from example) can return new expression (PExpr).
That's what do the Nemerle.Linq.ToExpression macro which converts lambdas to expression tree.

Liviu U

unread,
Aug 21, 2011, 2:59:19 AM8/21/11
to nemer...@googlegroups.com
Excellent, 

I will study that macro too.

Thanks for the guidance.

2011/8/21 VladD2 <v...@rsdn.ru>

--
Reply all
Reply to author
Forward
0 new messages