Tutorial (sprint2)

This tutorial explains how to use the Validation Engine of QInjection (version "Sprint 2 - Arechi").
I'll illustrate how to set up it for different kind of lambda expressions.

Understanding the Support and As methods

Internally the validation engine builds the AST (Abstract Syntax Tree) of the lambda expression and it walks throught it by validating each branch.
An AST is composed by Node and Branches. So declaring the syntax of a lambda expression means to define the "composition" of the Tree in term of Nodes, Branches and Links between them. This is made using two mains methods of the QInjector class:
  • Support(Node): this method sets the Node on which the declarations are applied on.
  • As(Branch): this method adds the Node as valid element on a specific branch.

For example following statement:

injector.Support(Node.IdentifierName).As(Branch.LeftMember).As(Branch.RightMember);


tells that the Node Node.IdentifierName can be accepted when exploring branches Branch.LeftMember and Branch.RightMember (left and right member of a binary expression).

The validation pattern "Accept All if not restricted"

By default the validation engine doesn't apply any restriction on the visited node.
On certain nodes you can apply restrictions using the methods:
  • WithName(String Name): InvocationExpression, MemberAccessExpression
  • WithNameRegex(String RegularExpressionDeclaration): InvocationExpression, MemberAccessExpression
  • WithOperator(Operator op): BinaryExpression, PreFixUnaryExpression, PostFixUnaryExpression
  • WithArity(Int32 Arity): InvocationExpression
  • RefuseReference(Reference reference): InvocationExpression

If you apply a restriction on a node than only "lamda expression" that conforms to the rule will be accepted.
Let me to clarify: for a Node BinaryExpression withouth restriction than any operator will accepted, but if you add restrictions on operators Plus (+) and Equals (==) then only lambda expression containing {+} and == inside binary expression will be accepted.

Syntax of a Validation Rule

A validation rule starts with the method Support(...) and can contains many As(...) and With*(...) methods invocation.

These are valid validation rules:
injector = injector.Support(Node.IdentifierName).As(Branch.Body);
injector = injector.Support(Node.PrefixUnaryExpression).As(Branch.Body).As(Branch.Expression)
        .WithOperator(Operator.LogicalNot);
injector = injector.Support(Node.PrefixUnaryExpression).As(Branch.Body).
        .WithOperator(Operator.LogicalNot).WithOperator(Operator.LogicalAnd);

These are not valid (or confusing) code samples:
injector = injector.As(Branch.Body);
injector = injector.WithOperator(Operator.LogicalNot);

Pay attention to the use of the Support(...); you have to write it before any As(...) or With*(...).
Each series of As(...) and With*(...) statements refer to the last Support(...) invocation.

Before to start

Every Syntax Declaration starts with following two mandatory lines:
    injector = injector.Support(Node.SimpleLambdaExpression).As(Branch.Input);
    injector = injector.Support(Node.Parameter).As(Branch.Parameter);

The first "branch" evaluated is the "input branch" and it has to be a Node.SimpleLambdaExpression.
The "Support(...).As(...)" on the node Node.Parameter is used for parsing the Parameter branch of the Lambda Expression.

Lambda Expression Guide

Now I'll list some common pattern for node validation.

Literal Expression

x => 1

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.LiteralExpression).As(Branch.Body);

Indentifier Expression

x => x

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.IdentifierName).As(Branch.Body);

Unary Expression

x => !x

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.PrefixUnaryExpressionSyntax
   Expression :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.PrefixUnaryExpression).As(Branch.Body)
        .WithOperator(Operator.LogicalNot);
    injector = injector.Support(Node.IdentifierName).As(Branch.Expression);

Binary Expression

x => x == 1

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
   LeftMember :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
   RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.BinaryExpression).As(Branch.Body)
        .WithOperator(Operator.Equals);
    injector = injector.Support(Node.IdentifierName).As(Branch.LeftMember);
    injector = injector.Support(Node.LiteralExpression).As(Branch.RightMember);

Conditional Expression (ternary)

x => x ? 1 : 0

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.ConditionalExpressionSyntax
   Condition :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
   WhenFalse :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
   WhenTrue :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.ConditionalExpression).As(Branch.Body);
    injector = injector.Support(Node.IdentifierName).As(Branch.Condition);
    injector = injector.Support(Node.LiteralExpression).As(Branch.WhenFalse)
        .As(Branch.WhenTrue);

Member Access Expression

x => x.item == 0

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.MemberAccessExpressionSyntax
   Expression :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.MemberAccessExpression).As(Branch.Body)
        .WithName("item");
    injector = injector.Support(Node.IdentifierName).As(Branch.Expression);

Invocation Expression

x => x.Check(x,x)

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.InvocationExpressionSyntax
   Argument :: Roslyn.Compilers.CSharp.ArgumentSyntax
    Expression :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
   Argument :: Roslyn.Compilers.CSharp.ArgumentSyntax
    Expression :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.InvocationExpression).As(Branch.Body)
        .WithName("Check").WithArity(2);
    injector = injector.Support(Node.Argument).As(Branch.Argument);
    injector = injector.Support(Node.IdentifierName).As(Branch.Expression);

Nested Binary Expression

x => x + 1 == 1

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
   LeftMember :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
    LeftMember :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
    RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
   RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.BinaryExpression).As(Branch.Body)
        .WithOperator(Operator.Equals).WithOperator(Operator.Plus);
    injector = injector.Support(Node.LiteralExpression).As(Branch.RightMember);
    injector = injector.Support(Node.BinaryExpression).As(Branch.LeftMember);
    injector = injector.Support(Node.IdentifierName).As(Branch.LeftMember);

Parenthesized Expression

x => (x == 1)

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.ParenthesizedExpressionSyntax
   Expression :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
    LeftMember :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
    RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
  Parameter :: Roslyn.Compilers.CSharp.ParameterSyntax

The Validation Code is:
    injector = injector.Support(Node.ParenthesizedExpression).As(Branch.Body);
    injector = injector.Support(Node.BinaryExpression).As(Branch.Expression)
        .WithOperator(Operator.Equals);
    injector = injector.Support(Node.LiteralExpression).As(Branch.RightMember);
    injector = injector.Support(Node.IdentifierName).As(Branch.LeftMember);

Nested Expression

x => x.Check(x ? x : 1, x + 1) && !x && (x + 1 == 2)

The AST generated by Roslyn is:
 Input :: Roslyn.Compilers.CSharp.SimpleLambdaExpressionSyntax
  Body :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
   LeftMember :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
    LeftMember :: Roslyn.Compilers.CSharp.InvocationExpressionSyntax
     Argument :: Roslyn.Compilers.CSharp.ArgumentSyntax
      Expression :: Roslyn.Compilers.CSharp.ConditionalExpressionSyntax
       Condition :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
       WhenFalse :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
       WhenTrue :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
     Argument :: Roslyn.Compilers.CSharp.ArgumentSyntax
      Expression :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
       LeftMember :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
       RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
    RightMember :: Roslyn.Compilers.CSharp.PrefixUnaryExpressionSyntax
     Expression :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
   RightMember :: Roslyn.Compilers.CSharp.ParenthesizedExpressionSyntax
    Expression :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
     LeftMember :: Roslyn.Compilers.CSharp.BinaryExpressionSyntax
      LeftMember :: Roslyn.Compilers.CSharp.IdentifierNameSyntax
      RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax
     RightMember :: Roslyn.Compilers.CSharp.LiteralExpressionSyntax

The Validation Code is:
    injector = injector.Support(Node.BinaryExpression).As(Branch.Body);

    injector = injector.Support(Node.InvocationExpression).As(Branch.LeftMember);
    injector = injector.Support(Node.PrefixUnaryExpression).As(Branch.RightMember);
    injector = injector.Support(Node.ParenthesizedExpression).As(Branch.RightMember);

    injector = injector.Support(Node.BinaryExpression).As(Branch.LeftMember);

    injector = injector.Support(Node.LiteralExpression).As(Branch.RightMember);
    injector = injector.Support(Node.IdentifierName).As(Branch.LeftMember);

    injector = injector.Support(Node.IdentifierName).As(Branch.Expression);

    injector = injector.Support(Node.Argument).As(Branch.Argument);
    injector = injector.Support(Node.BinaryExpression).As(Branch.Expression);
    injector = injector.Support(Node.ConditionalExpression).As(Branch.Expression);

    injector = injector.Support(Node.IdentifierName).As(Branch.Condition);
    injector = injector.Support(Node.IdentifierName).As(Branch.WhenTrue);
    injector = injector.Support(Node.LiteralExpression).As(Branch.WhenFalse);



Last edited Dec 29, 2013 at 10:49 PM by SamNium, version 1