Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Idea) Templates for code. ("active" parts considered by some as macros) #174

Closed
AdamSpeight2008 opened this issue Jan 30, 2015 · 7 comments
Labels
Area-Language Design Discussion Feature Request Language-C# Resolution-Won't Fix A real bug, but Triage feels that the issue is not impactful enough to spend time on.

Comments

@AdamSpeight2008
Copy link
Contributor

The use of this is a little specialised, mainly aimed at code that manipulates and analyses sourcecode.
Basic concept is to allow the coder to write the code they what to make as a Code Template and the compiler generates to code Roslyn's AST needed to make it.

How these would be expressed in code is up for discussion, as a base to start from I'm use the following. The type Expr<T> denotes an expression that evaluates to a T, a statement would be an Expr<Void>..

Codeplex Thread: Template Functions & Template Masks

Template Function

template  IFExpression _IF_ ( Expr<Bool> cond,  Expr truecode, Expr falsecode )
{
    if( /{ cond : Expr<Bool> }/ )
    {
      /{ truecode }/
    }
    else
    {
      /{ falsecode }/
    }
}

Template Mask

This example will use template functions and template mask to replace If - Else structure to Inline if structure.

This template mask matches against assignments.

template mask simple_assignment() : template_mask
{
  |{ /{ target }/ = /{ source }/ ; |}
}

This template generates an assignment that utilises and inline if expression. cond ? true : false

template assignment_viaCondition_ <T>( target : __      , cond   : Expr<Bool> ,
                                       valueA : Expr<T> , valueB : Expr<T> )  : CS.Expr
{
  |{ var /{ target }/ = (/{ cond }/ ) ? ( /{ valueA }/ ) : ( /{ valueB }/ ) ; |}
}
The following template mask matches against the if else construct.

template mask _IfElse_() : template_mask  
{
  |{ if( /{ cond }/ 
     {
       /{ on_true }/
     }
     else
     {
       /{ on_false }/
     }
   }|
}

So let's utilise the previously construct templates and template masks.

AnalyseNode ( node : SyntaxNode )
{

  var mn = template.Match( node , _IfElse_ )
  if( !mn.Valid ) return
  var part_0 = template.Match( mn["on_true"], simple_assignment )
  var part_1 = template.Match( mn["on_false"], simple_assignment )
  if( part_0.Valid && part_1.Valid )
  {
    if( part_0["target"] == part_1["target"] ) 
    { 
      node.ReplaceWith( assignment_viaCondition_( part_0["target"] , mn["cond"] , part_0["source"],  part_1["source"] );
    }
  }
}

Some of the functionality of Template Function can be implemented with the use of String Interpolation. Eg

SyntaxFactory.ParseExpression( 
  $"if( \{parameter.Identifier} == null ) { throw newArgumentNullException( nameof( \{parameter.IdentifieIdentifier.Text} )); }" );
@yume-chan
Copy link

Templates are better way to speed up coding than code snippets, because templates can be edit once and reflects in all usages, and you can have different templates in different project types.

Templates can be defined in namespaces and classes, like delegate, a demo for define template as follows:

namespace MyTemplates
{
    public template DependencyObject DefineProperty(raw type, raw name)
    {
        public DependencyProperty ${name}Property { get; } = DependencyProperty.Register("${name}", typeof(${type}), typeof(${scope.ClassName}), new PropertyMetadata(default(${type}));

        public ${type} ${name}
        {
            get { return (${type})GetValue(${name}Property); }
            set { SetValue(${name}Property, value); }
        }
    }
}

public is the access modifier of this template, so there will not be a whole world of templates.

template keyword indicates it's a template.

DependencyObject is the scope modifier to indicates will this template can be used, it must be one of the following values:

  • Type Name: This template can only be directly placed in a class of that type or derived from that type. This template can contain one or more fields, properties, methods, or even other templates, anything a class can have.
    • class keyword: Same as using object type, this template can be placed in any classes.
    • struct keyword: This template can be placed in any structs.
  • namespace keyword: This template can be placed in a namespace. This template can contain one or more classes, structs, enumerations, or even other templates.
    • These templates can also be placed in classes, as classes can also contain these types.
  • method keyword: This template can only be placed in method body, it should contain some useful logic for methods.

DefineProperty is the name of the template. Uses ${TemplateName(args)} to use this template in a valid scope.

raw type, raw name is the argument list of the template, raw is the type, it can be one of the following values:

  • Type Name: An object of this type must be given for using this template, same as a method argument.
  • raw keyword: Given as string, but when compiling, the quote marks are now included.
    • For the above demo, using ${DefineProperty("bool", "IsEnabled")}, public ${type} ${name} will be compiled to public bool IsEnabled.

In template body, uses ${argumentName} to reference an argument, when compiling, a normal type argument will be directly replaced, for example

template method Print(object value) { System.Console.Write(${value}); }

void Main() { ${Print(1)} }

compiles to

void Main() { System.Console.Write((object)1); }

Another example for method scope template:

template method TestNotNull(object parameter) { if (${parameter} == null) throw new ArgumentNullException(nameof(${parameter})); }

void SomeMethod(string stringArg) { ${TestNotNull(stringArg)} }

compiles to

void SomeMethod(string stringArg) { if ((object)stringArg == null) throw new ArgumentNullException(nameof(stringArg)); }

And in template body, a special object, scope, contains information of the current scope, for example when used in a class, scope.ClassName contains the name of the containing class.

Templates can be compiled as a special type into binaries, other project can reference this library to use these templates.

@gafter gafter added this to the Unknown milestone Sep 10, 2015
@gafter
Copy link
Member

gafter commented Sep 13, 2015

If I understand this, it is a proposal for a macro system.

@gafter gafter changed the title (Idea) Template Functions & Template Masks (Idea) Template Functions & Template Masks (macros) Sep 13, 2015
@gafter gafter removed this from the Unknown milestone Nov 20, 2015
@gafter
Copy link
Member

gafter commented Dec 9, 2015

Closing, as we do not want to add a macro system to C# of VB.net.

@gafter gafter closed this as completed Dec 9, 2015
@gafter gafter added the Resolution-Won't Fix A real bug, but Triage feels that the issue is not impactful enough to spend time on. label Dec 9, 2015
@AdamSpeight2008
Copy link
Contributor Author

@gafter It not entirely about macros. It's also about design-time code template (akin to VS snippets). It would allow "snippets" to be within a specific namespace, and not global. (In fact the current snippets ain't local, they are Visual Studio local. They are not tied to a specific project type, or even aware of the context they are to be pasted in (eg. #7858)

Yeah you could implement them as a code fix, but to that seem like a lot of code. Just to paste in section of code. In principle the ide and compiler can conspire together and automatically do the work for you.

We could also have usage attributes associated with the template, so that the IDE would be informed to when to suggest the snippet.

@AdamSpeight2008 AdamSpeight2008 changed the title (Idea) Template Functions & Template Masks (macros) (Idea) Templates for code. ("active" parts considered by some as macros) Jan 20, 2016
@Ytrog
Copy link

Ytrog commented Mar 9, 2016

If I understand correctly this proposal is a bit like defmacro in Common Lisp. Or is this more like inline T4?

@AdamSpeight2008
Copy link
Contributor Author

@Ytrog
Bit like inline T4 (minus the active parts), plus the template is validated by compiler.
So you'd get errors at design-time. ie it's not a string but code.

@masaeedu
Copy link

@gafter Is there any limited subset of macros you would be willing to support (e.g. variadic templates)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Language Design Discussion Feature Request Language-C# Resolution-Won't Fix A real bug, but Triage feels that the issue is not impactful enough to spend time on.
Projects
None yet
Development

No branches or pull requests

7 participants