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.pswp.cn/news/281812.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281812.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281812.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

斷電后supervisor啟動時報錯

當設備意外斷電后&#xff0c;supervisor在系統啟動時報錯&#xff0c;導致程序假死&#xff0c;此問題可以用systemd來替換supervisor。轉載于:https://www.cnblogs.com/gaoyiping/p/6950470.html

Vue單頁面在ios10系統上出現白屏的bug

一個bug 你用Vue做了一個單頁面應用&#xff0c;它在一切設備上都工作正常&#xff0c;但是突然有一天&#xff0c;你的測試和你說&#xff0c;這個網站在iOS 10上跑不起來&#xff0c;怎么辦&#xff1f;于是你打開你電腦上的Chrome瀏覽器&#xff0c;工作正常&#xff1b;打開…

HTTP/2 規格制定完成

IETF HTTP工作者的負責人Mark Nottingham在其博客上宣布HTTP/2規格制定完成&#xff0c;接下來將是分配RFC編號和正式發表。HTTP是Web的核心技術之一&#xff0c;相比HTTP/1&#xff0c;HTTP/2的改進之處包括更快的頁面加載&#xff1b;更長久的連接&#xff1b;服務器推送&…

【習題 6-7 UVA - 804】Petri Net Simulation

【鏈接】 我是鏈接,點我呀:) 【題意】 在這里輸入題意 【題解】 模擬就好 【代碼】 /* 1.Shoud it use long long ? 2.Have you ever test several sample(at least therr) yourself? 3.Can you promise that the solution is right? At least,the main ideal 4.use the p…

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…

java用log.i打印數組_java-使用JSCH將ssh日志打印到列表(android)

我一直試圖通過將包含我目錄名稱的log.i字符串值添加到數組中,然后使用數組適配器通過listView打印它們,來在listView中的目錄中打印項目.但是,當我嘗試運行該應用程序時,它將打印包含目錄名稱的日志,但不會在listView上打印任何內容.有什么幫助嗎&#xff1f;這是我的代碼&…

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…

java中的path類_詳談java中File類getPath()、getAbsolutePath()、getCanonical的區別

簡單看一下描述&#xff0c;例子最重要。1、getPath()&#xff1a;返回定義時的路徑&#xff0c;(就是你寫什么路徑&#xff0c;他就返回什么路徑)2、getAbsolutePath()&#xff1a;返回絕對路徑&#xff0c;但不會處理“.”和“..”的情況3、getCanonicalPath()&#xff1a;返…

部署站點支持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…

.NET Core Onvif協議C#教程系列之XiaoFeng.Onvif組件庫

物聯網IOT大背景下音視頻領域的Onvif 協議在2008年成為全球性的開放接口標準。于是一批開發者涌入該技術領域使用各種編程語言對其改造升級封裝。因為是國際標準&#xff0c;所以規范內容比較多&#xff0c;物聯網領域涉及又廣&#xff0c;所以研究協議是一個很耗時間的一項工作…

php異常處理的深入

引出 如果你調一個類&#xff0c;調用時數據驗證時報了個錯&#xff0c;你會以什么方式返回 數組&#xff0c;布爾值&#xff1f; 數組這個可以帶錯誤原因回來&#xff0c;那布爾值呢&#xff1f; 返回了個 false, 報錯時把錯誤放在類變量里&#xff1f;還是專門用一個獲取錯誤…

C# 跨平臺的支付類庫ICanPay

隨著微軟的開源&#xff0c;越來越多的項目支持跨平臺&#xff0c;但是各種支付平臺提供的類庫&#xff0c;又老又不支持跨平臺&#xff0c;吐槽下&#xff0c;尤其是微信&#xff0c;還有好多坑&#xff0c;于是ICanPay誕生了&#xff0c;今天就來講ICanPay是什么&#xff0c;…

.NET CORE 下收發郵件之 MAILKIT

背景利用代碼發送郵件在工作中還是比較常見的&#xff0c;相信大家都用過SmtpClient來處理發送郵件的操作&#xff0c;不過這個類以及被標記已過時&#xff0c;所以介紹一個微軟推薦的庫MailKit來處理。MailKit開源地址&#xff1a;https://github.com/jstedfast/MailKit需要郵…

【20181026T2】**圖【最小瓶頸路+非旋Treap+啟發式合并】

題面 【錯解】 最大最小&#xff1f;最小生成樹嘛 蛤&#xff1f;還要求和&#xff1f; 點分治&#xff1f; 不可做啊 寫了個MST暴力LCA&#xff0c;30pts&#xff0c;140多行 事后發現30分是給dijkstra的 woc 【正解】 樹上計數問題&#xff1a;①并查集②啟發式合并③點分治 …