dotnet 用 SourceGenerator 源代码生成技术实现中文编程语言

相信有很多伙伴都很喜欢自己造编程语言,在有现代的很多工具链的帮助下,实现一门编程语言,似乎已不是一件十分困难的事情。我利用 SourceGenerator 源代码生成技术实现了一个简易的中文编程语言,核心原理是将中文编程语言翻译为 C# 语言,从而完成后续的所有对接,完成了最简单的构建和运行。本文将告诉大家这个有趣的方式是如何实现

开始之前,先给大家看看效果

b898c5a375fd4c27ad4b559bc3290c0a.jpeg

这是我设计的 csg 格式(Chinese programming language by SourceGenerator)的中文编程语言,设计上完全参考(抄袭)了中文宏的实现方式。原本我是考虑抄袭 易语言 的,但是 易语言 更贴近是 VB 系的方式(?似乎也不能这么说)感觉不是我随便就能写出来的。我只是想着学习源代码生成技术,顺带测试一下自己能否很随意的就写出一个新的编程语言。当然,测试结果是我不能很随意就写出一个新的编程语言

本文所设计的 csg 格式的中文编程语言,仅仅只能用来做演示使用,丝毫不能用在实际项目里。本文仅仅只是用来告诉大家一个简易的方法来完成自己创建一门编程语言

本文所设计的 csg 格式的中文编程语言,能够和 C# 完美的结合,毕竟实际参与构建的就是 C# 代码。我在本文的最后给出了所有的代码的下载方式,要求在 VS 2022 较新版本上才能成功运行

以下是 csg 的代码,也是本文效果里所使用的代码

引用命名空间 系统;定义命名空间 这是一个命名空间;类型 这是测试类型
{公开的 静态的 无返回值类型的 测试输出(){控制台.输出一行文本("你好");}
}

可以看到,这是全部采用中文编写的一段代码。相信大家看到上面的代码,在熟悉 C# 的前提下,能反应过来这段代码的作用

尽管这是采用中文编写的,但不代表着任何人都能读懂这段代码的作用。因为这仅仅只是使用中文对 C# 的关键词进行翻译而已。同理的,也不是任何会英文的人都能读懂代码

那以上代码可以被如何调用呢?可以完全和 C# 交互,被 C# 直接调用,如以下代码,在 C# 代码的主函数里面调用 测试输出() 方法。这是利用了 C# 里面允许标识符支持 Utf-8 编写,而不仅仅是 ASCII 编码的字符。换句话说是使用中文作用方法名、类名、属性名等,在 C# 里都是合法的

// Program.csusing 这是一个命名空间;这是测试类型.测试输出();

以上是采用 C# 9.0 新特性——顶级语句,无须加上类型和主函数定义,直接编写代码体即主函数执行代码体的。如此可以极大简化代码量

执行代码,可以看到控制台输出了 你好 字符串,证明了代码的构建执行正常

接下来将告诉大家实现的原理和实现的细节方法,在开始之前,期望大家已对 C# dotnet 的基础知识熟悉,对 dotnet 整个构建过程熟悉,了解源代码生成技术,本文将略过基础知识

先新建两个项目,分别是 JelallnalukebaqeLairjaybearjair 和 JelallnalukebaqeLairjaybearjair.Analyzers 两个控制台项目。其中 JelallnalukebaqeLairjaybearjair 项目就是用来编写中文编程的项目。而 JelallnalukebaqeLairjaybearjair.Analyzers 是一个分析器项目,将在此项目里编写源代码生成逻辑,用来支持将编写的中文代码转换为 C# 代码,从而参与后续的构建和执行

在 JelallnalukebaqeLairjaybearjair 项目里,将对 JelallnalukebaqeLairjaybearjair.Analyzers 项目进行引用,从而用来启动此分析器的内容。添加引用时设置 OutputItemType 为 Analyzer 类型,且设置不使用不引用 JelallnalukebaqeLairjaybearjair.Analyzers 程序集。引用之后的 JelallnalukebaqeLairjaybearjair 项目的 csproj 项目文件的引用代码如下

<ItemGroup><ProjectReference Include="..\JelallnalukebaqeLairjaybearjair.Analyzers\JelallnalukebaqeLairjaybearjair.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /></ItemGroup>

在本文的例子里,在 JelallnalukebaqeLairjaybearjair 项目里只有两个文件,一个是 Program.cs 文件,一个是 这是测试类型.csg 文件。其中 Program.cs 文件就是传统的 C# 项目,采用 C# 9.0 的顶层语句,编写的代码如下

using 这是一个命名空间;这是测试类型.测试输出();

而 这是测试类型.csg 文件里的内容就是本文开头的中文代码内容

接着,为了让分析器能了解到 这是测试类型.csg 文件是需要参与构建的,额外在 JelallnalukebaqeLairjaybearjair 的 csproj 项目文件里面添加 AdditionalFiles 列表。通过 AdditionalFiles 列表,可以在后续的分析器里面,在增量构建里,通过 AdditionalTextsProvider 监听获取到这部分文件内容。编辑 JelallnalukebaqeLairjaybearjair 的 csproj 项目文件,添加如下代码

<ItemGroup><AdditionalFiles Include="这是测试类型.csg" /></ItemGroup>

以上就是 JelallnalukebaqeLairjaybearjair 项目的所有文件和核心逻辑了。完成了准备工作之后,开始编写 JelallnalukebaqeLairjaybearjair.Analyzers 分析器项目。为了能够在 Visual Studio 里面加载上分析器,以及同时在 dotnet 命令行里加载分析器,设置 TargetFramework 为 .NET Standard 2.0 版本。因为 Visual Studio 采用的是 .NET Framework 运行时,而 dotnet 命令行工具采用的是 .NET Core 运行时,于是分析器采用 .NET Standard 2.0 版本就能刚好在这两个运行时加载

为了编写分析器项目,按照惯例,还需要引用必要的 NuGet 包。这里需要引用 Microsoft.CodeAnalysis.Analyzers 和 Microsoft.CodeAnalysis.CSharp 程序集

编辑 JelallnalukebaqeLairjaybearjair.Analyzers 的 csproj 项目文件为如下代码

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>netstandard2.0</TargetFramework><AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" /><PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" /></ItemGroup></Project>

完成了安装库之后,即可开始编写核心代码。需求是将 csg 格式的中文编程语言,转换为 C# 代码,从而参与后续的构建和执行

新建一个叫 CsgIncrementalGenerator 类型,继承 IIncrementalGenerator 接口,顺带加上 GeneratorAttribute 特性标识这是生成 C# 代码的。类型名可以自己发挥,只是本文作为例子叫成 CsgIncrementalGenerator 而已

[Generator(LanguageNames.CSharp)]public class CsgIncrementalGenerator : IIncrementalGenerator{// 忽略代码}

继承 IIncrementalGenerator 接口,需要实现 public void Initialize(IncrementalGeneratorInitializationContext context) 方法。如 尝试 IIncrementalGenerator 进行增量 Source Generator 生成代码 博客所述,在进行增量构建时,只有 Initialize 方法。在 Initialize 方法里面,加上分析器感兴趣的文件以及对这些文件的处理方法即可

咱这里的中文编程语言采用后缀名为 .csg 的文件,在 JelallnalukebaqeLairjaybearjair 项目里也将 csg 文件在 csproj 项目文件里添加到 AdditionalFiles 列表里面。在 Initialize 方法里面,先告诉分析器感兴趣的文件就是 csg 文件,只有有 csg 文件的变更,那将自动触发更新逻辑,在更新逻辑里执行实际的转换代码

public void Initialize(IncrementalGeneratorInitializationContext context){var csgFileIncrementalValuesProvider =context.AdditionalTextsProvider.Where(t =>string.Equals(Path.GetExtension(t.Path), ".csg", StringComparison.OrdinalIgnoreCase));// 忽略文件}

以上代码的 AdditionalTextsProvider 不是实际立刻提供了文件,而是用来编写文件变更时的过滤命令,这也是增量代码生成的核心逻辑。通过编写过滤命令的方式,可以减少代码生成实际转换逻辑的执行次数,只有在遇到感兴趣的文件的变更的时候才会触发实际的执行逻辑,从而极大的提升性能

接下来将此过滤条件加入注册,在过滤条件 csgFileIncrementalValuesProvider 能过滤出有文件变更时,将执行转换代码。转换代码的输入是 csg 中文编程语言的代码文件,输出是加入到构建的 C# 的代码字符串

通过 RegisterSourceOutput 方法进行注册,注册在满足 csgFileIncrementalValuesProvider 过滤条件时,支持添加额外的参与构建代码

context.RegisterSourceOutput(csgFileIncrementalValuesProvider, (sourceProductionContext, csg) =>{// 忽略代码});

在 RegisterSourceOutput 的开始,是先注册框架部分的代码,如上面的中文代码,可以看到用到了一些需要预设的框架代码,例如 控制台.输出一行文本("你好"); 这句代码就需要先有预设的名为 控制台 的类型。先添加框架代码如下

context.RegisterSourceOutput(csgFileIncrementalValuesProvider, (sourceProductionContext, csg) =>{AddFrameworkCode(sourceProductionContext);// 忽略代码});

这里拿到的 sourceProductionContext 参数,可以用来设置构建的生成代码。在 AddFrameworkCode 里面,添加框架需要的预设代码,代码如下

/// <summary>/// 添加框架代码/// </summary>/// <param name="sourceProductionContext"></param>private static void AddFrameworkCode(SourceProductionContext sourceProductionContext){string consoleText = @"
using System;namespace 系统;static class 控制台
{public static void 输出一行文本(string 文本){Console.WriteLine(文本);}
}";sourceProductionContext.AddSource("DefaultConsole", consoleText);}

本文这里只添加了用来演示的名为 控制台 的类型,添加方法如上代码。以上代码将会在项目里,添加一个叫做 DefaultConsole 的生成代码,如此即可让中文编程代码里有可以使用的控制台辅助类型

接下来是获取到发生变更的 csg 中文编程语言的文件的内容,用来转换为 C# 代码

context.RegisterSourceOutput(csgFileIncrementalValuesProvider, (sourceProductionContext, csg) =>{AddFrameworkCode(sourceProductionContext);var csgSource = csg.GetText();if (csgSource == null) return;// 忽略代码});

通过 GetText 即可获取到其文本内容

获取到内容之后,需要将 csg 中文编程语言的内容转换为 C# 代码字符串内容。我这里抄袭了中文宏的方法,使用关键词替换。本文这里只是替换了演示所需要的关键词,没有对其他的关键词进行替换

var keyDictionary = new Dictionary<string, string>(){{"引用命名空间 ","using "},{"定义命名空间 ","namespace "},{"类型 ","class "},{"公开的 ","public "},{"静态的 ","static "},{"无返回值类型的 ","void "},};var stringBuilder = new StringBuilder();foreach (var textLine in csgSource.Lines){var text = textLine.ToString();if (!string.IsNullOrEmpty(text)){foreach (var keyValuePair in keyDictionary){text = text.Replace(keyValuePair.Key, keyValuePair.Value);}}stringBuilder.AppendLine(text);}

如此一行行进行替换,即可拿到一段 C# 代码

将 stringBuilder 里的 C# 代码作为生成代码,添加到 sourceProductionContext 用于参与构建

sourceProductionContext.AddSource(Path.GetFileNameWithoutExtension(csg.Path) + ".g.cs", stringBuilder.ToString());

添加的时候,设置了 hintName 参数为 Path.GetFileNameWithoutExtension(csg.Path) + ".g.cs" 如此即可在相同的一个 csg 文件变更的时候,生成的代码可以替换旧的生成代码。生成代码之间的替换就是采用 hintName 参数作为判断条件

如此即可完成将 csg 中文编程语言转换为 C# 代码,且加入到构建里

本文只是作为一个演示,告诉大家可以利用 Source Generator 技术,将中文编程语言转换为 C# 代码,方便的加入到构建里,从而复用整个 dotnet 的机制

本文的代码放在github 和 gitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin bba0c728bbc1d850f6f1929ab14a42e995e23e3b

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin bba0c728bbc1d850f6f1929ab14a42e995e23e3b

获取代码之后,进入 JelallnalukebaqeLairjaybearjair 文件夹

更多增量构建请看 尝试 IIncrementalGenerator 进行增量 Source Generator 生成代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTP/2 规格制定完成

IETF HTTP工作者的负责人Mark Nottingham在其博客上宣布HTTP/2规格制定完成&#xff0c;接下来将是分配RFC编号和正式发表。HTTP是Web的核心技术之一&#xff0c;相比HTTP/1&#xff0c;HTTP/2的改进之处包括更快的页面加载&#xff1b;更长久的连接&#xff1b;服务器推送&…

easyui combobox java_Easyui的combobox实现动态数据级联效果

实现从数据库中动态获取对应的list集合&#xff0c;并在easyui的combobox中显示出来。实现的效果如下&#xff1a;1、数据库的表设计如图所示2、数据库中填写相关的数据&#xff0c;如图所示。如图所示【法律法规】是所属栏目&#xff0c;因此他的字段parentid是0。【中国公民出…

为什么应该默认将 Class 设为密封类?

前言最近在 dotnet/sdk 上看到一个 Issue&#xff0c;它提出了一个有趣的要求&#xff1a;默认情况下将类设置为密封类(Sealed)&#xff1f;什么是密封类&#xff1f;默认情况下&#xff0c;类是开放的&#xff0c;这意味着它是可以被继承的。例如&#xff1a;class BaseClass …

Spring工具类的使用

2019独角兽企业重金招聘Python工程师标准>>> Spring-core中提供了大量的工具类&#xff0c;常用的有StringUtils、ObjectUtils、NumberUtils、Base64Utils等&#xff0c;Spring工具类在spring-core.jar中的org.springframework.util包下。 org.springframework.util…

python作业高级FTP(第八周)

作业需求&#xff1a; 1. 用户加密认证 2. 多用户同时登陆 3. 每个用户有自己的家目录且只能访问自己的家目录 4. 对用户进行磁盘配额、不同用户配额可不同 5. 用户可以登陆server后&#xff0c;可切换目录 6. 查看当前目录下文件 7. 上传下载文件&#xff0c;保证文件一致性 8…

Edge 浏览器被爆存在 XSS 绕过漏洞

来自知名安全测试套件Burp Suite厂商PortSwigger的安全专家Gareth Heyes近日在微软Edge浏览器的内置XSS过滤器存在绕过漏洞&#xff0c;这就意味着尽管微软在Edge浏览器中进行了大量的安全策略部署&#xff0c;但用户浏览网页的时候依然有可能让攻击者通过这种方式在Edge浏览器…

来了!十大更新

面向 Windows 10 正式版用户&#xff0c;微软发布了 2022 年 10 月更新。Windows 10 版本 21H1 更新后操作系统内部版本升级至 Build 190432130/2132&#xff08;带外更新&#xff09;。Windows 10 版本 21H2 更新后操作系统内部版本升级至 Build 19044.2130/2132&#xff08;带…

使用ansible 批量分发SSH Key

先确保你电脑有ansible&#xff0c;我是mac的用brew安装,其他可用yum安装brew search ansiblebrew install ansible我已经安装好2.7了生成下自己的key&#xff0c;一路回车ssh-keygen -t rsa编辑host&#xff0c;添加需要增加ssh key的机器vi /etc/ansible/hosts【hostgroup】我…

使用Git简单笔记

这里只是作为简单的笔记整理&#xff0c;第一次使用的推荐先看一下廖大的教程&#xff0c;内容很多很细&#xff0c;可以边看边练、看不懂的地方先记着、争取七七八八看下来。 心情不佳的分割线 廖雪峰的git教程&#xff1a; https://www.liaoxuefeng.com/wiki/001373951630592…

部署站点支持Https访问的方法

1、申请公钥和私钥&#xff0c;放到服务器 2、编辑default配置文件 改为 加上证书路径 ps:泛域名支持admin.xxx.com、demo.xxx.com等等&#xff0c;而免费的Lets Encrypt仅支持www.xxx.com和xxx.com 整理自www.laravist.com转载于:https://www.cnblogs.com/lamp01/p/6952464.ht…

Hadoop/HDFS命令

Hadoop中文手册&#xff1a;http://hadoop.apache.org/docs/r1.0.4/cn/commands_manual.html 英文手册&#xff1a;http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/CommandsManual.html Hadoop fs命令 hadoop fs: 该命令可以用于其他文件系统&#x…

内置IOC容器ServiceCollection

.NetCore内置IOC容器ServiceCollection一、IOC介绍IOC&#xff1a;全名(Inversion of Control)-控制反转IOC意味着我们将对象的创建控制权交给了外部容器&#xff0c;我们不管它是如何创建的&#xff0c;我们只需要知道&#xff0c;当我们想要某个实例时&#xff0c;我们可以直…

React+Redux仿Web追书神器

引言 由于 10 月份做的 React Native 项目没有使用到 Redux 等库&#xff0c;写了一段时间想深入学习 React&#xff0c;有个想法想做个 demo 练手下&#xff0c;那时候其实还没想好要做哪一个类型的&#xff0c;也看了些动漫的&#xff0c;小说阅读&#xff0c;聚合资源的开源…

windows配置solr5.5.2(不通过tomcat,使用内置jetty)

一、windows下配置solr5.5.2(不通过tomcat,使用内置jetty) 第一步&#xff1a;安装Jdk1.7 Solr5.5好像只支持Jdk1.7及以上的版本&#xff0c;没亲测&#xff0c;solr6.0是只支持jdk1.8及以上的&#xff0c;下图为启动solr时的截图&#xff1a; 如何在windows环境下配置jdk及验证…

【C# Personal Handbook】运行环境

一、CLR、CLI、CTS、CLS、BCL、FCL简介CLI&#xff08;公共语言基础&#xff09;CLI是微软公司向ECMA提交的一份语言和数据格式规范&#xff0c;CLR是目前为止唯一一个公共语言基础的实现版本。CLI包括了公共类型系统&#xff08;CTS&#xff09;、公共中间语言&#xff08;CIL…

如何完善自己的知识结构

★领域 &#xff08;本来想用“学科”这个词&#xff0c;后来觉得“学科”的范畴还是偏小&#xff0c;就改用“领域”&#xff09;  按照传统的习惯&#xff0c;通常会把知识归类到不同的领域&#xff08;比如&#xff1a;文学、数学、计算机、烹调、等等&#xff09;。 ◇领…

java调c++代码_Java中调用C++代码的实现 | 学步园

JNI为 Java Native Interface 即Java本地接口&#xff0c;使用此种方式可以对C/C代码进行调用&#xff0c;其在本质上是对C/C生成的动态库进行调用而不是直接对C/C代码进行调用Java代码如下&#xff1a;public class TestJNI{//JNI在本质上是调用C/C的动态库来实现的&#xff…

jeesite1.X 集成多数据源

2019独角兽企业重金招聘Python工程师标准>>> 网上看了几个例子&#xff0c;都是相同数据源的动态切换&#xff0c;如果不是同一种数据库类型&#xff0c;分页查询就出问题。经过研究解决问题。 jeesite.properties配置多数数据源地址,这里以mysql5.7和sqlserver2008…

k8s HPA(HorizontalPodAutoscaler)-自动水平伸缩

Horizontal Pod Autoscaling in Kubernetes写在前面我们平时部署web服务&#xff0c;当服务压力大撑不住的时候&#xff0c;我们会加机器(加钱)&#xff1b;一般没有上容器编排是手动加的&#xff0c;临时加的机器&#xff0c;临时部署的服务还要改Nginx的配置&#xff0c;最后…

spark java 逻辑回归_逻辑回归分类技术分享,使用Java和Spark区分垃圾邮件

原标题&#xff1a;逻辑回归分类技术分享&#xff0c;使用Java和Spark区分垃圾邮件由于最近的工作原因&#xff0c;小鸟很久没给大家分享技术了。今天小鸟就给大家介绍一种比较火的机器学习算法&#xff0c;逻辑回归分类算法。回归是一种监督式学习的方式&#xff0c;与分类类似…