開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么

這篇文章主要講解了“開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么”吧!

創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)整合營銷推廣、網(wǎng)站重做改版、望奎網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、成都h5網(wǎng)站建設(shè)、成都商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為望奎等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

一、   引子

 監(jiān)控畫面的主要功能之一就是跟蹤下位機變量變化,并將這些變化展現(xiàn)為動畫。大部分時候,界面上一個圖元組件的某個狀態(tài),與單一變量Tag綁定,比如電機的運行態(tài),綁定一個MotorRunning信號;但有些時候不會這么簡單,比如溫度計在溫度高于50℃顯示紅色;某設(shè)備報警,可能是多個條件其中之一觸發(fā)的結(jié)果;變量變化觸發(fā)一系列連鎖反應(yīng)…如此種種??紤]到工控行業(yè)大部分技術(shù)人員并非計算機專業(yè)出身,如何能夠用最少的編碼解決各種復(fù)雜的變量-動畫綁定問題,無疑要費一番心思。

開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么

二、   方案選型

針對變量動畫綁定問題,可以選擇的方案包括如下幾種:

  • 腳本編譯器

不少大型組態(tài)軟件包含強大的腳本編輯器,支持諸如VBS、Python甚至C腳本語言。腳本自帶語法編輯器、調(diào)試器和編譯器,調(diào)用的API包羅萬象,如數(shù)據(jù)庫API,通訊API,畫面組態(tài)API…可以用腳本實現(xiàn)非常復(fù)雜的邏輯。

但基于下面幾種考慮,我沒有實現(xiàn)這類的腳本編譯器:

  1. 不同于大部分組態(tài)軟件包含一個獨立的界面設(shè)計器,我用Visual Studio來肩挑語法編輯、調(diào)試、編譯和界面設(shè)計的重任,沒必要多此一舉的搞一個獨立的腳本編譯器。

  2. C#結(jié)合Visual Studio來調(diào)用通訊、數(shù)據(jù)庫鏈接的各類函數(shù),C#包含強大的語法功能,配合.NET 類庫幾乎無所不能,同時C#也支持腳本化,沒有必要在使用其他腳本語言。

對于復(fù)雜的邏輯,就讓C#配合VS神器來完成吧。

  • 運算符重載。

曾經(jīng)研究過一個C#寫的腳本編譯系統(tǒng),它可以實現(xiàn)兩個特定集合間的四則運算和邏輯運算,如List1.A+List2.A;List1.A>List2.B??瓷先ゼ暇拖褚粋€普通的數(shù)值那樣參與運算和操作。

運算符重載是C#一個強大的語法功能,可以重載的操作符如下:

運算符

可重載性

+、-、!、~、++、--、true、false

可以重載這些一元運算符。
  true和false運算符必須成對重載。

+、-、*、/、%、&、|、^、<<、>>

可以重載這些二元運算符。

==、!=、<、>、<=、>=

可以重載比較運算符。必須成對重載。

&&、||

不能重載條件邏輯運算符。
  但可以使用能夠重載的&和|進(jìn)行計算。

[]

不能重載數(shù)組索引運算符,但可以定義索引器。

()

不能重載轉(zhuǎn)換運算符,但可以定義新的轉(zhuǎn)換運算符。

+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=

不能顯式重載賦值運算符。
  在重寫單個運算符如+、-、%時,它們會被隱式重寫。

=、.、?:、->、new、is、sizeof、typeof


無疑運算符重載用的好可以寫出語義更清晰、更簡潔的代碼。

比如有一種復(fù)數(shù)類型Complex,有兩個坐標(biāo)x和y;定義ComplexA大于ComplexB為: A的x,y中至少有一個大于B的x,y。我只需要重載>操作符(相應(yīng)的最好重載>=,<,<=),以后只需要A>B就能代替重復(fù)啰嗦的A.x>B.x||A.y>B.y。更可喜的是,重載后的>,<這些運算符,在.Net表達(dá)式樹(ExpressionTree)中已經(jīng)替換了它原來的語義。因此運算符重載在我這個編譯器也有它用武之地。

但出于下面兩個原因,它只適合作為編譯引擎的輔助,而不適合單獨使用:

  1. 首先運算符重載只針對特定的類型;對于不熟悉C#語法特性的編程者,理解并正確的使用運算符重載不是件容易的事。

  2. 運算符重載可以減少重復(fù)的代碼,讓語法更簡潔;但依然要寫C#代碼,不適合大部分工控人員。

  •  訂閱事件

如果想省事,最簡單的辦法是直接寫代碼,例如:如果一臺電機的運行需要A,B,C三個前提條件均滿足,我就分別訂閱A、B、C的變量變化事件,如果A由fasle變?yōu)閠rue,再看看其他兩個變量觸發(fā)沒有。也就是寫這樣幾行代碼:

           var tag1 = App.Server["A"];

            var tag2 = App.Server["B"];

            var tag3 = App.Server["C"];

            if (tag1 != null && tag2 != null && tag3 != null

            {

                tag1.ValueChanged += (s, e) =>

                  {

                      if (tag1.Value.Boolean && tag2.Value.Boolean && tag3.Value.Boolean)

                      {

                          //執(zhí)行

                      }

                  };

                tag2.ValueChanged += (s, e) =>

                {

                    if (tag1.Value.Boolean && tag2.Value.Boolean && tag3.Value.Boolean)

                    {

                        //執(zhí)行

                    }

                };

                tag3.ValueChanged += (s, e) =>

                {

                    if (tag1.Value.Boolean && tag2.Value.Boolean && tag3.Value.Boolean)

                    {

                        //執(zhí)行

                    }

                };

            }

看上去不算復(fù)雜吧?如果界面上有50個動畫,這樣的代碼就要寫50次。不但浪費時間,改起來麻煩,查起來也麻煩。更糟糕的是,不懂編程的人還用不了。

  •  表達(dá)式編譯器

對于大部分零編程基礎(chǔ)的上位機設(shè)計人員,他們需要的是一種沒有學(xué)習(xí)和理解成本的、簡單直觀的變量綁定方式。

比如溫度計在溫度高于50℃顯示紅色,就一句話【temperature>50】;某設(shè)備顯示報警,可能是多個報警變量其中之一觸發(fā)的結(jié)果,只需寫【Alarm1||Alarm2||Alarm3】…借助微軟強大的表達(dá)式引擎,如果能解析這類變量表達(dá)式,設(shè)計者只需要知道圖元與變量的邏輯關(guān)系;而極少數(shù)表達(dá)式也難以企及的功能,略微懂一點C#就可以實現(xiàn)。這樣就可以做到使用簡單,上手容易,同時又可以滿足復(fù)雜的需求。

同時還有下面幾個額外的好處:

最少的編碼量:在一個界面的cs文件里,幾乎沒有代碼。綁定邏輯在XAML內(nèi)用直觀的方式嵌入:

 開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么

  1. 可以用復(fù)制、粘貼和文本替換等功能減少重復(fù)編碼;

  2. 可以充分利用WPF的設(shè)計器擴(kuò)展,實現(xiàn)一個簡單的語法編輯器,實現(xiàn)語法高亮、自動完成并執(zhí)行語法檢查;

  3. 查找變量邏輯和修改很方便。

這個編譯器的主要代碼在Eval類。

三、   自己實現(xiàn)一個編譯器

  • 編譯原理

大學(xué)計算機都有一門編譯原理課程。當(dāng)年我也捧著一本教材,被“波蘭表達(dá)式”、“逆波蘭表達(dá)式”繞的云里霧里,然而逆波蘭表達(dá)式是實現(xiàn)編譯器的關(guān)鍵。

逆波蘭表達(dá)式的優(yōu)勢在于只用兩種簡單操作,入棧和出棧就可以搞定任何普通表達(dá)式的運算。其運算方式如下:

如果當(dāng)前字符為變量或者為數(shù)字,則壓棧,如果是運算符,則將棧頂兩個元素彈出作相應(yīng)運算,結(jié)果再入棧,最后當(dāng)表達(dá)式掃描完后,棧里的就是結(jié)果。

如何實現(xiàn)自己的編譯器,微軟已經(jīng)給大家現(xiàn)成的輪子了。微軟的Expression類提供了一套拼接、編譯Lambda表達(dá)式的完整方法,可以用它輕松定義你自己的語法。相關(guān)知識可以參考博客園 裝配腦袋 的自己動手開發(fā)編譯器系列文章:http://www.cnblogs.com/Ninputer/archive/2011/06/18/2084383.html。下面就以這個SCADA項目為例:

  • 定義語法

在這一版,我只實現(xiàn)了最基本最常用的一些操作,如四則運算(+-*/)、邏輯運算(&|!)、取反取模、三目條件等運算。

GetOperatorLevel函數(shù)按照C#的運算符優(yōu)先級定義運算優(yōu)先級。

定義了@開頭的自定義函數(shù)如@Date取當(dāng)前日期、@App取當(dāng)前路徑等。

IsConstant方法定義系統(tǒng)常數(shù),其中True/False表示邏輯常量,字符串常量用’’。

  • 編譯過程

編譯過程就是將一個字符串轉(zhuǎn)換為一個帶返回值的函數(shù);函數(shù)的參數(shù)就是表達(dá)式相關(guān)的Tag的值。依次為:

  1. RpnExpression方法:將中綴表達(dá)式轉(zhuǎn)換為逆波蘭表達(dá)式。用關(guān)鍵字將表達(dá)式字符串分割為一個數(shù)組;按照優(yōu)先級出棧入棧;返回一個逆波蘭表達(dá)式順序的字符串列表。

  2. ComplieRpnExp方法:根據(jù)逆波蘭表達(dá)式順序,依次彈出運算符轉(zhuǎn)換為Expression的各子類如二元表達(dá)式BinaryExpression、條件表達(dá)式ConditionalExpression、常數(shù)表達(dá)式ConstantExpression等;參數(shù)首先判斷是否常數(shù),如果不是,則調(diào)用GetTagExpression方法,將字符串轉(zhuǎn)換為方法調(diào)用MethodCallExpression,最終會將該參數(shù)編譯為一個Tag。經(jīng)過處理最終返回一個LambdaExpression。

  3. Eval方法將LambdaExpression編譯為一個委托;相關(guān)的Tag加入列表TagList。

四、   應(yīng)用場景

  •   表達(dá)式與動畫綁定

在每一個界面窗體都有幾乎一樣的幾行代碼:

List<TagNodeHandle> _valueChangedList;

        private void HMI_Loaded(object sender, RoutedEventArgs e)

        {

            lock (this)

            {

                _valueChangedList = cvs1.BindingToServer(App.Server);

            }

        }

        private void HMI_Unloaded(object sender, RoutedEventArgs e)

        {

            lock (this)

            {

               App.Server.RemoveHandles(_valueChangedList);

            }

        }

其中, BindingToServer就是對當(dāng)前界面所有圖元進(jìn)行地毯式掃描,搜索出各控件相關(guān)的TagReadText表達(dá)式并用Eval類編譯之;編譯的結(jié)果轉(zhuǎn)換為帶返回值的函數(shù)和一個相關(guān)Tag的列表;遍歷這個Tag列表,將其值變化事件ValueChanged與這個函數(shù)鏈接起來。這樣,在加載界面的時候已經(jīng)完成了編譯過程,相關(guān)變量的值一旦改變,就會根據(jù)表達(dá)式返回一個值,如果這個值是布爾量,同時與電機的運行動畫綁定,就完成了從表達(dá)式到動畫的觸發(fā)過程。

  •   復(fù)雜報警條件

報警一般包括超限報警、變量觸發(fā)報警、差值報警等。但也可能有復(fù)雜的報警條件,不能用超限、超差等簡單方式表述的,就可以歸結(jié)為復(fù)雜報警,其條件可以用類似動畫綁定的表達(dá)式來描述,在系統(tǒng)初始化時刻加載、編譯為報警條件。

  •  未來改進(jìn)

編輯器改進(jìn):支持命令自動完成、語法高亮、更完善的語法檢查??煽紤]Sharpdevelop的編輯控件。

支持復(fù)雜語法:目前的語法僅僅是簡單的四則運算和邏輯表達(dá)式。未來考慮支持多段表達(dá)式、函數(shù)(如正余弦)、屬性引用等復(fù)雜語法。

感謝各位的閱讀,以上就是“開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

網(wǎng)頁名稱:開源純C#表達(dá)式編譯器的實現(xiàn)方法是什么
文章地址:http://muchs.cn/article10/ighogo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊、網(wǎng)站排名營銷型網(wǎng)站建設(shè)、網(wǎng)站收錄、網(wǎng)站內(nèi)鏈、品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運營