一尘不染

有没有办法在C#中实现自定义语言功能?

c#

我已经对此感到困惑了一段时间,并且四处张望,无法找到有关该主题的任何讨论。

假设我想实现一个简单的示例,例如一个新的循环结构:do..until

写得非常类似。

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

这样做可以将其转换为有效的csharp:

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

这显然是一个简单的示例,但是有什么方法可以添加这种性质的东西吗?理想情况是作为Visual Studio扩展来启用语法突出显示等。


阅读 461

收藏
2020-05-19

共1个答案

一尘不染

Microsoft提出将Rolsyn
API作为带有公共API的C#编译器的实现。它为每个编译器管道阶段包含单独的API:语法分析,符号创建,绑定,MSIL发出。您可以提供自己的语法解析器实现或扩展现有的语法解析器实现,以便获得具有所需功能的C#编译器。

罗斯林CTP

让我们使用Roslyn扩展C#语言!在我的示例中,我将替换带有相应的do-while的do-until语句:

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));
//                }
//            }

现在,我们可以使用Roslyn API编译更新的语法树,或将语法Root.GetFullText()保存到文本文件并将其传递给csc.exe。

2020-05-19