日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

NET Core中使用Irony实现自己的查询语言语法解析器

發(fā)布時(shí)間:2023/12/4 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NET Core中使用Irony实现自己的查询语言语法解析器 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在之前《在ASP.NET Core中使用Apworks快速開(kāi)發(fā)數(shù)據(jù)服務(wù)》一文的評(píng)論部分,.NET大神張善友為我提了個(gè)建議,可以使用Compile As a Service的Roslyn為語(yǔ)法解析提供支持。在此非常感激友哥給我的建議,也讓我了解了一些Roslyn的知識(shí)。使用Roslyn的一個(gè)很大的好處是,框架無(wú)需依賴(lài)第三方的組件,并且Roslyn也是.NET Foundation的一個(gè)開(kāi)源項(xiàng)目,為.NET語(yǔ)言提供編譯服務(wù),社區(qū)支持做的也非常出色。然而,經(jīng)過(guò)一段時(shí)間的思考,我還是選擇了一個(gè)折中的方案:在Apworks中使用Irony作為查詢(xún)語(yǔ)言的語(yǔ)法解析器,與此同時(shí),為查詢(xún)語(yǔ)言語(yǔ)法解析提供可擴(kuò)展的框架級(jí)支持。

那么問(wèn)題來(lái)了:為什么我需要在Apworks中設(shè)計(jì)查詢(xún)語(yǔ)言?Irony是什么?如何使用Irony實(shí)現(xiàn)自己的查詢(xún)語(yǔ)言語(yǔ)法解析器?下面我就一一為大家介紹。

Apworks中的查詢(xún)語(yǔ)言

很多體驗(yàn)過(guò)Apworks數(shù)據(jù)服務(wù)(Apworks Data Services)案例:TaskList的讀者肯定有這樣的感受:為什么每次我新建的任務(wù)項(xiàng)目(Task Item)都是出現(xiàn)在列表中不確定的位置?難道新建的任務(wù)就不應(yīng)該放在最前面嗎?是的,你的疑問(wèn)沒(méi)有錯(cuò),在之前的TaskList中,的確存在這樣的問(wèn)題,因?yàn)槟菚r(shí)候Apworks數(shù)據(jù)服務(wù)在返回任務(wù)列表時(shí),還不支持查詢(xún)和排序,也就是說(shuō),它只能默認(rèn)以Id作為升序進(jìn)行分頁(yè),返回所有的數(shù)據(jù)。當(dāng)然,在最近一版的Apworks數(shù)據(jù)服務(wù)中,通過(guò)基于Irony的語(yǔ)法解析器,已經(jīng)能夠成功地支持查詢(xún)和排序了。

如果你之前有仔細(xì)閱讀《在ASP.NET Core中使用Apworks快速開(kāi)發(fā)數(shù)據(jù)服務(wù)》一文,并按照文中的演練步驟實(shí)現(xiàn)過(guò)一個(gè)簡(jiǎn)單的RESTful服務(wù)的話(huà),那么,請(qǐng)你重新在Visual Studio 2017中打開(kāi)你的解決方案,將Apworks相關(guān)庫(kù)更新到最新版本,然后不要修改任何代碼,直接運(yùn)行你的應(yīng)用。等應(yīng)用程序運(yùn)行后,執(zhí)行一次GET請(qǐng)求,URL中你就可以使用query作為查詢(xún)條件輸入了。比如,使用curl執(zhí)行下面的命令:



curl -G "http://localhost:58928/api/customers" --data-urlencode "query=name sw \"fr\""

你將得到下面的結(jié)果:

可以看到,數(shù)據(jù)服務(wù)返回了所有Name字段以“fr”開(kāi)頭的客戶(hù)信息。當(dāng)然,還支持排序操作。比如執(zhí)行下面的命令:


curl -G "http://localhost:58928/api/customers" --data-urlencode "sort=name d"

將得到下面的結(jié)果:

此時(shí)返回結(jié)果已經(jīng)按Name字段倒序排列。

在Apworks中,查詢(xún)語(yǔ)言支持以下操作和運(yùn)算:

  • 邏輯運(yùn)算:AND OR NOT

  • 關(guān)系運(yùn)算:EQ(相等),NE(不等),LT(小于),LE(小于等于),GT(大于),GE(大于等于)

  • 字符串運(yùn)算:SW(以某字符串開(kāi)頭)、EW(以某字符串結(jié)尾)、CT(包含某字符串)

  • 括號(hào)優(yōu)先級(jí)

  • 日期類(lèi)型的比對(duì)

排序語(yǔ)言支持升序(用字母a表示)以及降序(用字母d表示),多個(gè)排序條件使用AND關(guān)鍵字連接。例如:name a AND email d,表示使用name字段做升序排序,并以email做降序排序。

以上就給大家大概介紹了一下Apworks數(shù)據(jù)服務(wù)對(duì)查詢(xún)和排序的支持功能。設(shè)計(jì)這部分功能的需求是顯而易見(jiàn)的:開(kāi)發(fā)人員無(wú)需為一般的查詢(xún)和排序功能自定義額外的接口。或許你會(huì)問(wèn),為何不使用已有的框架,比如OData。不錯(cuò),OData的確可以提供統(tǒng)一的查詢(xún)界面,做系統(tǒng)集成也會(huì)相對(duì)容易,但一方面我還是覺(jué)得OData太重,Apworks數(shù)據(jù)服務(wù)我希望能夠提供更加簡(jiǎn)單便捷的功能;另一方面,看上去目前OData還不支持.NET Core(應(yīng)該是不支持,我不太確定,有知道的朋友也歡迎留言指正)。

實(shí)現(xiàn)這套查詢(xún)和排序語(yǔ)法,我使用的是一個(gè).NET下開(kāi)源的語(yǔ)法解析器生成工具集,它的名字叫做Irony。

Irony簡(jiǎn)介

Irony項(xiàng)目最開(kāi)始是發(fā)布在微軟的Codeplex代碼托管服務(wù)上的,地址是:http://irony.codeplex.com/。在Codeplex上的好評(píng)數(shù)有51顆星,也已經(jīng)很不錯(cuò)了。可惜的是,最近一次更新是在2013年12月,看起來(lái)已經(jīng)停止維護(hù)了,不過(guò)之前使用了一下,感覺(jué)這個(gè)項(xiàng)目確實(shí)不錯(cuò),不僅提供了開(kāi)發(fā)庫(kù),而且還有一個(gè)圖形化的語(yǔ)法解析器的測(cè)試工具,在寫(xiě)完自己的自定義語(yǔ)言的語(yǔ)法之后,還可以通過(guò)這個(gè)工具進(jìn)行測(cè)試。于是,我把它遷移到了Github,成為我的一個(gè)公共repo,地址是:https://github.com/daxnet/irony。當(dāng)然,我沿用了原有的MIT許可協(xié)議,并在首頁(yè)的README.md中提供了原始地址(很可惜Codeplex將在年底關(guān)閉),并保留了開(kāi)發(fā)者的名字。不僅如此,在一番踩坑之后,我把它遷移到了.NET Core平臺(tái)。

在我的Irony Github Repo里,提供了一個(gè)非常簡(jiǎn)單的案例,就是實(shí)現(xiàn)四則混合運(yùn)算的字符串解析,并計(jì)算最終結(jié)果。當(dāng)然,這個(gè)案例也被包含在了這個(gè)項(xiàng)目的源代碼里。大家可以自己下載查看。

Irony的一個(gè)特色就是運(yùn)用了C#的運(yùn)算符重載,使得語(yǔ)法定義借用了C#的編譯功能(語(yǔ)法、類(lèi)型檢查等),簡(jiǎn)單直觀(guān),又不容易出錯(cuò)。比如,在如下案例中的語(yǔ)法定義類(lèi)型中:


[Language( "xpression Grammar" , "1.0" , "abc" )] public class ExpressionGrammar : Grammar { ???? /// <summary> ???? /// Initializes a new instance of the <see cref="ExpressionGrammar"/> class. ???? /// </summary> ???? public ExpressionGrammar() : base ( false ) ???? { ???????? var number = new NumberLiteral( "Number" ); ???????? number.DefaultIntTypes = new TypeCode[] { TypeCode.Int16, TypeCode.Int32, TypeCode.Int64 }; ???????? number.DefaultFloatType = TypeCode.Single; ???????? var identifier = new IdentifierTerminal( "Identifier" ); ???????? var comma = ToTerm( "," ); ???????? var BinOp = new NonTerminal( "BinaryOperator" , "operator" ); ???????? var ParExpr = new NonTerminal( "ParenthesisExpression" ); ???????? var BinExpr = new NonTerminal( "BinaryExpression" , typeof (BinaryOperationNode)); ???????? var Expr = new NonTerminal( "Expression" ); ???????? var Term = new NonTerminal( "Term" ); ???????? var Program = new NonTerminal( "Program" , typeof (StatementListNode)); ???????? Expr.Rule = Term | ParExpr | BinExpr; ???????? Term.Rule = number | identifier; ???????? ParExpr.Rule = "(" + Expr + ")" ; ???????? BinExpr.Rule = Expr + BinOp + Expr; ???????? BinOp.Rule = ToTerm( "+" ) | "-" | "*" | "/" ; ???????? RegisterOperators(10, "+" , "-" ); ???????? RegisterOperators(20, "*" , "/" ); ???????? MarkPunctuation( "(" , ")" ); ???????? RegisterBracePair( "(" , ")" ); ???????? MarkTransient(Expr, Term, BinOp, ParExpr); ???????? this .Root = Expr; ???? } }

從中可以很容易理解:運(yùn)算符(BinOp)包含+、-、*和/,而一個(gè)二元運(yùn)算的表達(dá)式(BinExpr)由兩個(gè)表達(dá)式(Expr)和一個(gè)運(yùn)算符(BinOp)組成,而二元運(yùn)算的表達(dá)式又是表達(dá)式(Expr)的一種。通過(guò)這樣的語(yǔ)法定義,就可以使用Irony的Parser產(chǎn)生語(yǔ)法樹(shù)了:


var language = new LanguageData( new ExpressionGrammar()); var parser = new Parser(language); var syntaxTree = parser.Parse(input);

怎么樣,是不是非常方便?

在遷移Irony項(xiàng)目的同時(shí),我還將Irony的測(cè)試工具Irony Grammar Explorer分離出來(lái)成為了一個(gè)單獨(dú)的Github Repo。在你定義了上面的ExpressionGrammar類(lèi)之后,編譯你的程序集,然后就可以使用Irony Grammar Explorer進(jìn)行測(cè)試了。比如,使用Irony Grammar Explorer打開(kāi)Apworks.Querying.Parsers.Irony程序集,它將自動(dòng)掃描程序集中所有的Grammar定義,然后讓用戶(hù)對(duì)各種Grammar進(jìn)行測(cè)試。值得一提的是,在測(cè)試界面,Irony Grammar Explorer還能根據(jù)語(yǔ)法定義,自動(dòng)產(chǎn)生語(yǔ)法高亮:

點(diǎn)擊右邊的語(yǔ)法樹(shù)中的節(jié)點(diǎn),即可定位到輸入字符串的相應(yīng)部分。比較有趣的一點(diǎn)是,在Irony Grammar Explorer的Github Repo里,還包含了一個(gè)語(yǔ)法定義的案例庫(kù):IronyExplorer.Samples,它包含了很多流行編程語(yǔ)言的語(yǔ)法定義。比如,下面是C# 3.5語(yǔ)言的語(yǔ)法測(cè)試效果:

有關(guān)Irony Grammar Explorer的其它功能,我就不一一介紹了,大家可以自己實(shí)踐一下。總的來(lái)說(shuō),Irony可以幫助大家快速方便地實(shí)現(xiàn)語(yǔ)法解析器,而且功能也能夠滿(mǎn)足絕大多數(shù)需求,針對(duì).NET Core的支持,也使得Irony能夠直接被應(yīng)用在跨平臺(tái)的.NET應(yīng)用程序中,并支持Docker部署。接下來(lái)的問(wèn)題就更有趣了:我已經(jīng)定義了自己的語(yǔ)法,并使用Irony Grammar Explorer通過(guò)了測(cè)試,接下來(lái),我如何在我的應(yīng)用程序中運(yùn)用這個(gè)語(yǔ)法?換個(gè)方式問(wèn):我拿到了語(yǔ)法樹(shù)后,該怎么辦呢?

語(yǔ)法樹(shù)的處理

雖然我們能夠?qū)⒆址谋窘馕龀梢豢谜Z(yǔ)法樹(shù),能夠通過(guò)語(yǔ)法樹(shù)來(lái)體現(xiàn)一個(gè)字符串中各個(gè)部分的含義,以及它們之間的關(guān)系,但是如何能夠讓計(jì)算機(jī)來(lái)讀懂這棵樹(shù),并執(zhí)行相應(yīng)的任務(wù)呢?這就涉及到語(yǔ)法樹(shù)的處理問(wèn)題。參考編譯原理,詞法分析和語(yǔ)法分析已經(jīng)由Irony完成,接下來(lái)的語(yǔ)義分析,就需要我們自己寫(xiě)代碼了。

在Irony Repo的案例代碼中,我們的目的是能夠解析一個(gè)四則運(yùn)算表達(dá)式,并計(jì)算出結(jié)果,于是,我們定義了下面的對(duì)象模型:

frameborder="0" scrolling="no" style="border-width: medium; width: 650px; height: 494px;">

因此,只需要將解析的語(yǔ)法樹(shù)轉(zhuǎn)換成上面的對(duì)象模型,也就能夠通過(guò)Evaluation.Value屬性,得到計(jì)算的最終結(jié)果。從代碼上看,向?qū)ο竽P偷霓D(zhuǎn)換,是通過(guò)遞歸的方式遍歷語(yǔ)法樹(shù)實(shí)現(xiàn)的:



private Evaluation PerformEvaluate(ParseTreeNode node) { ?? switch (node.Term.Name) ?? { ???? case "BinaryExpression" : ???????? var leftNode = node.ChildNodes[0]; ???????? var opNode = node.ChildNodes[1]; ???????? var rightNode = node.ChildNodes[2]; ???????? Evaluation left = PerformEvaluate(leftNode); ???????? Evaluation right = PerformEvaluate(rightNode); ???????? BinaryOperation op = BinaryOperation.Add; ???????? switch (opNode.Term.Name) ???????? { ???????????? case "+" : ???????????????? op = BinaryOperation.Add; ???????????????? break ; ???????????? case "-" : ???????????????? op = BinaryOperation.Sub; ???????????????? break ; ???????????? case "*" : ???????????????? op = BinaryOperation.Mul; ???????????????? break ; ???????????? case "/" : ???????????????? op = BinaryOperation.Div; ???????????????? break ; ???????? } ???????? return new BinaryEvaluation(left, right, op); ???? case "Number" : ???????? var value = Convert.ToSingle(node.Token.Text); ???????? return new ConstantEvaluation(value); ?? } ?? throw new InvalidOperationException($ "Unrecognizable term {node.Term.Name}." ); }

以上完整代碼請(qǐng)參考Evaluator的實(shí)現(xiàn)。整個(gè)案例及使用方式可以點(diǎn)擊https://github.com/daxnet/irony#example查看。可以看到,使用Irony來(lái)實(shí)現(xiàn)一個(gè)四則混合運(yùn)算的計(jì)算器還是非常方便的。

在Apworks中,我們需要的是能夠?qū)⒁粋€(gè)表達(dá)查詢(xún)語(yǔ)義的語(yǔ)法樹(shù),轉(zhuǎn)換成Lambda表達(dá)式,以便于后臺(tái)數(shù)據(jù)庫(kù)引擎能夠直接執(zhí)行Lambda表達(dá)式完成查詢(xún)。通過(guò)數(shù)據(jù)庫(kù)引擎執(zhí)行Lambda表達(dá)式的優(yōu)勢(shì)是非常明顯的,比如Entity Framework Core可以通過(guò)Lambda表達(dá)式生成高效的SQL語(yǔ)句并在數(shù)據(jù)庫(kù)服務(wù)器上執(zhí)行,性能方面也能兼顧得非常好。

類(lèi)似的,我們使用.NET Expression的對(duì)象模型,通過(guò)遍歷查詢(xún)語(yǔ)句的語(yǔ)法樹(shù)來(lái)生成表達(dá)式模型,最后轉(zhuǎn)換成Lambda表達(dá)式即可。具體過(guò)程就不再贅述了,請(qǐng)參考Apworks的源代碼。現(xiàn)在我們來(lái)看看實(shí)際效果。

假設(shè)我們的測(cè)試數(shù)據(jù)如下:



Customers.Add( new Customer { Id = 1, Email = "jim@example.com" , Name = "jim" , DateRegistered = DateTime.Now.AddDays(-1) }); Customers.Add( new Customer { Id = 2, Email = "tom@example.com" , Name = "tom" , DateRegistered = DateTime.Now.AddDays(-2) }); Customers.Add( new Customer { Id = 3, Email = "alex@example.com" , Name = "alex" , DateRegistered = DateTime.Now.AddDays(-3) }); Customers.Add( new Customer { Id = 4, Email = "carol@example.com" , Name = "carol" , DateRegistered = DateTime.Now.AddDays(-4) }); Customers.Add( new Customer { Id = 5, Email = "david@example.com" , Name = "david" , DateRegistered = DateTime.Now.AddDays(-5) }); Customers.Add( new Customer { Id = 6, Email = "frank@example.com" , Name = "frank" , DateRegistered = DateTime.Now.AddDays(-6) }); Customers.Add( new Customer { Id = 7, Email = "peter@example.com" , Name = "peter" , DateRegistered = DateTime.Now.AddDays(-7) }); Customers.Add( new Customer { Id = 8, Email = "paul@example.com" , Name = "paul" , DateRegistered = DateTime.Now.AddDays(1) }); Customers.Add( new Customer { Id = 9, Email = "winter@example.com" , Name = "winter" , DateRegistered = DateTime.Now.AddDays(2) }); Customers.Add( new Customer { Id = 10, Email = "julie@example.com" , Name = "julie" , DateRegistered = DateTime.Now.AddDays(3) }); Customers.Add( new Customer { Id = 11, Email = "jim@example.com" , Name = "jim" , DateRegistered = DateTime.Now.AddDays(4) }); Customers.Add( new Customer { Id = 12, Email = "brian@example.com" , Name = "brian" , DateRegistered = DateTime.Now.AddDays(5) }); Customers.Add( new Customer { Id = 13, Email = "david@example.com" , Name = "david" , DateRegistered = DateTime.Now.AddDays(6) }); Customers.Add( new Customer { Id = 14, Email = "daniel@example.com" , Name = "daniel" , DateRegistered = DateTime.Now.AddDays(7) }); Customers.Add( new Customer { Id = 15, Email = "jill@example.com" , Name = "jill" , DateRegistered = DateTime.Now.AddDays(8) });

下面調(diào)試單元測(cè)試,并查看所產(chǎn)生的Lambda表達(dá)式,可以看到,Lambda表達(dá)式正確產(chǎn)生,測(cè)試順利通過(guò):

總結(jié)

本文介紹了Apworks中自定義查詢(xún)語(yǔ)句在Apworks數(shù)據(jù)服務(wù)中的應(yīng)用,并介紹了查詢(xún)語(yǔ)句和排序語(yǔ)句的實(shí)現(xiàn)方式,與此同時(shí)對(duì)Irony Grammar Parser進(jìn)行了介紹。Apworks中查詢(xún)語(yǔ)句的實(shí)現(xiàn)還是相對(duì)簡(jiǎn)單的,目前不支持內(nèi)嵌對(duì)象的屬性查詢(xún),比如Customer.Address.Country EQ “China” 這樣的查詢(xún)是不支持的。為了保證實(shí)現(xiàn)過(guò)程相對(duì)簡(jiǎn)單快速,今后也不打算支持。如果需要用到這種內(nèi)嵌對(duì)象屬性的查詢(xún),請(qǐng)擴(kuò)展DataServiceController以實(shí)現(xiàn)自己的特定API來(lái)完成。

接下來(lái)我會(huì)介紹Entity Framework Core在Apworks數(shù)據(jù)服務(wù)中的使用(雖然已經(jīng)預(yù)告了好幾次了-_-!!)。

原文地址:http://www.cnblogs.com/daxnet/p/6953418.html


.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注

總結(jié)

以上是生活随笔為你收集整理的NET Core中使用Irony实现自己的查询语言语法解析器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。