深入理解Visual FA的源代码生成器

在本文中,将深入探讨Visual FA的源代码生成器,这是一个强大的工具,它允许开发者在编译时注入额外的动态生成的代码。这种技术被称为源代码生成器,它为C#语言提供了额外的功能扩展,尽管使用起来可能有些奇特。

C#9引入了一种能力,允许在C#编译过程中挂钩编译器,并在编译二进制文件的过程中注入额外的动态创建的代码。这项技术被称为源代码生成器,它们是增强语言功能的强大方式,即使偶尔使用起来有些古怪。

使用代码

源代码生成器的核心是FARuleAttribute

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] class FARuleAttribute : System.Attribute { public FARuleAttribute() { } public FARuleAttribute(string expression) { if (expression == null) throw new ArgumentNullException(nameof(expression)); if (expression.Length == 0) throw new ArgumentException("The expression must not be empty", nameof(expression)); Expression = expression; } public string Expression { get; set; } = ""; public string BlockEnd { get; set; } = null; public int Id { get; set; } = -1; public string Symbol { get; set; } = null; }

请注意,不会在库中或在源代码中找到这个属性。它是在第一次使用它之后注入到代码中的,任何支持类型也是如此。因此,当第一次使用这些时,代码中会出现红色波浪线。只需构建,因为只要代码没有问题,波浪线就会自行解决。

定义规则

在代码中,定义了MyLexer方法,它有四个规则。每个规则都有一个正则表达式——通常是第一个未命名参数,然后是几个可选的命名参数:BlockEndIdSymbol。每个参数代表词法分析器中的一个规则。

[FARule(@"\/\*", Id = 0, Symbol = "commentBlock", BlockEnd = @"\*\/")] [FARule(@"\/\/[^\n]*", Id = 1, Symbol = "commentLine")] [FARule(@"[ \t\r\n]+", Id = 2, Symbol = "whiteSpace")] [FARule(@"[A-Za-z_][A-Za-z0-9_]*", Id = 3, Symbol = "identifier")] internal partial FAStringRunner MyLexer(string text);

这里,定义了commentBlockcommentLinewhiteSpaceidentifier作为符号,并为每个规则分配了ID,尽管不必这样做——如果未提供,它们将在生成过程中填充。如果不记得块结束的目的,请再次查看系列的第一篇文章,但基本上,它是一个用于匹配多字符结束条件的附加表达式。

方法签名

方法签名本身必须是一个部分方法,在部分类中。它必须要么不接受参数,接受单个string参数,或者接受单个TextReader参数。它必须返回FAStringRunnerFATextReaderRunnerFAStringDfaTableRunnerFATextReaderDfaTableRunner。如果函数接受一个参数,也可以返回FARunner

本质上,返回类型用于确定要生成哪种类型的运行器,例如,它应该在字符串上操作还是在文本读取器上操作,以及它是编译的还是表格驱动的。

使用类标记

另一种选择是标记一个类。这样做的好处是让可以访问最终运行器的类型,包括它的符号常量(如果在定义中使用了Symbol):

[FARule(@"\/\*", Id = 0, Symbol = "commentBlock", BlockEnd = @"\*\/")] [FARule(@"\/\/[^\n]*", Id = 1, Symbol = "commentLine")] [FARule(@"[ \t\r\n]+", Id = 2, Symbol = "whiteSpace")] [FARule(@"[A-Za-z_][A-Za-z0-9_]*", Id = 3, Symbol = "identifier")] partial class MyLexer : FAStringRunner { }

然后可以像这样使用它:

var exp = "the 10 quick brown #@%$! foxes jumped over 1.5 lazy dogs"; var runner = new MyLexer(); runner.Set(exp); foreach (var match in runner) { Console.WriteLine(match); }

还可以执行类似if(match.SymbolId == FooLexer.whiteSpace)的操作。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485