有没有C#实现自定义语言功能的方法吗?(Is there a way to implement cu

2019-06-17 14:48发布

我一直在纳闷这一段时间,我已经四处张望了一下,找不到这个话题的讨论。

让我们假设我想实现一个简单的例子,像一个新的循环结构:do..until

写的很相似do..while

do {
    //Things happen here
} until (i == 15)

这可以通过这样转化为有效CSHARP:

do {
    //Things happen here
} while (!(i == 15))

这显然是一个简单的例子,但有什么办法可以增加这种性质的东西吗? 理想的情况是作为一个Visual Studio扩展,使语法高亮显示等。

Answer 1:

微软提出Rolsyn API为C#编译器的公共API的实现。 它包含了每个编译器流水线阶段的个体的API:语法分析,符号创建,装订,MSIL排放。 您可以提供自己的句法分析器的或扩展现有一个为了得到C#编译器瓦特/你想的任何功能。

罗斯林CTP

让我们用罗斯林延长C#语言! 在我的例子,我做替换,直到声明W /对应的DO-同时:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers.CSharp;

namespace RoslynTest
{

    class Program
    {
        static void Main(string[] args)
        {

            var code = @"

            using System;

            class Program {
                public void My() {
                    var i = 5;
                    do {
                        Console.WriteLine(""hello world"");
                        i++;
                    }
                    until (i > 10);
                }
            }
            ";



            //Parsing input code into a SynaxTree object.
            var syntaxTree = SyntaxTree.ParseCompilationUnit(code);

            var syntaxRoot = syntaxTree.GetRoot();

            //Here we will keep all nodes to replace
            var replaceDictionary = new Dictionary<DoStatementSyntax, DoStatementSyntax>();

            //Looking for do-until statements in all descendant nodes
            foreach (var doStatement in syntaxRoot.DescendantNodes().OfType<DoStatementSyntax>())
            {
                //Until token is treated as an identifier by C# compiler. It doesn't know that in our case it is a keyword.
                var untilNode = doStatement.Condition.ChildNodes().OfType<IdentifierNameSyntax>().FirstOrDefault((_node =>
                {
                    return _node.Identifier.ValueText == "until";
                }));

                //Condition is treated as an argument list
                var conditionNode = doStatement.Condition.ChildNodes().OfType<ArgumentListSyntax>().FirstOrDefault();

                if (untilNode != null && conditionNode != null)
                {

                    //Let's replace identifier w/ correct while keyword and condition

                    var whileNode = Syntax.ParseToken("while");

                    var condition = Syntax.ParseExpression("(!" + conditionNode.GetFullText() + ")");

                    var newDoStatement = doStatement.WithWhileKeyword(whileNode).WithCondition(condition);

                    //Accumulating all replacements
                    replaceDictionary.Add(doStatement, newDoStatement);

                }

            }

            syntaxRoot = syntaxRoot.ReplaceNodes(replaceDictionary.Keys, (node1, node2) => replaceDictionary[node1]);

            //Output preprocessed code
            Console.WriteLine(syntaxRoot.GetFullText());

        }
    }
}
///////////
//OUTPUT://
///////////
//            using System;

//            class Program {
//                public void My() {
//                    var i = 5;
//                    do {
//                        Console.WriteLine("hello world");
//                        i++;
//                    }
//while(!(i > 10));
//                }
//            }

现在我们可以使用罗斯林API编译更新过的语法树或保存syntaxRoot.GetFullText()为文本文件,并把它传递给CSC.EXE。



Answer 2:

大遗漏片挂钩到管道,否则你一起比是没有太大的进一步.Emit提供。 别误会,罗斯林带来了很多伟大的事情,但对于我们这些谁想要实现预处理器和元编程,看来现在是不是上盘。 您可以实现“代码的建议”或他们称之为“问题” /“行动”作为扩展名,但是这基本上是一次性的代码转换,可以作为一个建议的在线更换, 不是你们想实现一个新的语言的方式是什么特征。 这是你总是可以用做扩展,但罗斯林使代码分析/转型带来极大的便利:

从我读过的从上CodePlex上论坛罗斯林开发商的意见,提供挂钩插入管道一直没有最初的目标。 所有的他们已经在C#6预览提供新的C#语言功能涉案修改罗斯林本身。 所以你基本上是需要到餐桌罗斯林。 他们对如何建立罗斯林与Visual Studio的测试文档。 这将是叉罗斯林和有Visual Studio中使用它重手的方式。 我说的重手,因为现在谁想要使用新的语言功能必须与你替换默认的编译器。 你可以看到这将开始变得混乱。

罗斯林建设,并与自己的身材替换的Visual Studio 2015年预览的编译器

另一种方法是建立一个编译器,它充当代理罗斯林。 有用于构建VS可以利用编译器的标准API。 这不是一个简单的任务,但。 你会在代码文件中读取,于罗斯林的API调用来转换语法树,放出的结果。

与代理方法的另一个挑战是要越来越智能感知与任何新的语言功能,您实现发挥很好。 你可能得有C#的你的“新”的变种,使用不同的文件扩展名,并执行所有的Visual Studio需要智能感知工作的API。

最后,考虑到C#的生态系统,有什么可扩展的编译器的意思。 比方说,罗斯林没有支持这些挂钩,这是作为提供NuGet包或VS扩展,以支持新的语言功能一样简单。 所有你的C#的利用新的DO-直到特性本质上是无效的C#,且无需使用您的自定义扩展将无法编译。 如果你走得足够远沿着这条路有足够多的人实现新的功能,很快你会发现不兼容的语言功能。 也许有人实现了一个预处理宏的语法,但它不能沿侧别人的新语法,因为它们发生在使用类似的语法来描绘宏观的开始使用。 如果你充分利用了很多开源项目,并发现自己挖掘到他们的代码,你会遇到很多奇怪的语法,将要求你身边的跟踪和研究项目是利用特定的语言扩展。 这可能是疯狂。 我的意思不是听起来像一个唱反调,因为我有很多的想法,语言功能和我对此很感兴趣,但应该考虑这意味着什么,以及它是如何维护的是。 试想一下,如果你被雇佣到地方工作,他们已经实施了各种新语法的,你必须学习,没有处于被审核这些功能以同样的方式C#的功能有,你可以打赌,他们中有些人也可以没有设计/实施。



Answer 3:

您可以检查www.metaprogramming.ninja (我是开发者),它提供了一种简单的方法来完成语言扩展(我提供的构造,性能,甚至JS风格的函数的例子)以及基于全面的语法的DSL。

该项目是开源的为好。 您可以找到文档,示例等github上 。

希望能帮助到你。



Answer 4:

你不能在C#创建自己的语法抽象,所以你能做的最好的就是创建自己的高阶函数。 你可以创建一个Action扩展方法:

public static void DoUntil(this Action act, Func<bool> condition)
{
    do
    {
        act();
    } while (!condition());
}

您可以为使用:

int i = 1;
new Action(() => { Console.WriteLine(i); i++; }).DoUntil(() => i == 15);

虽然这是值得怀疑这是否是最好使用do..while直接。



Answer 5:

没有就没有办法达到什么you'are谈论。

原因你问的就是定义新的语言结构,因此新的词法分析,语言分析,语义分析,编译和生成的优化是什么IL

在这种情况下,做的是使用一些宏/功能。

public bool Until(int val, int check)
{
   return !(val == check);
}

并使用它像

do {
    //Things happen here
} while (Until(i, 15))


Answer 6:

我发现扩展C#语言最简单的方法就是使用T4文本处理器进行预处理我的源代码。 T4的脚本会读我的C#,然后调用基于罗斯林分析器,它会生成自定义生成的代码的新来源。

在编译的时候,我所有的T4脚本会被执行,从而有效的工作为扩展预处理器。

在你的情况下,没有兼容的C#代码可输入如下:

#if ExtendedCSharp
     do 
#endif
     {
                    Console.WriteLine("hello world");
                    i++;
     }
#if ExtendedCSharp
                until (i > 10);
#endif

这将允许语法程序的开发过程中检查你的(C#标准)代码的其余部分。



文章来源: Is there a way to implement custom language features in C#?