亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        可嵌入C++的腳本語言的設(shè)計(jì)與實(shí)現(xiàn)

        2019-01-02 03:50:38劉永紅趙衛(wèi)東葉安勝
        關(guān)鍵詞:腳本語言運(yùn)算符詞法

        鄢 濤, 曾 誼, 孟 飛, 劉永紅, 趙衛(wèi)東, 葉安勝

        (1.成都大學(xué) 信息科學(xué)與工程學(xué)院, 四川 成都 610106;2.成都大學(xué) 模式識別與智能信息處理四川省高校重點(diǎn)實(shí)驗(yàn)室, 四川 成都 610106)

        0 引 言

        C++是一種高效率的編譯型程序設(shè)計(jì)語言,在硬件、游戲及桌面程序等領(lǐng)域的開發(fā)中有廣泛的應(yīng)用[1-2].如果源程序代碼被更改,則程序員首先需要重新編譯代碼,然后關(guān)閉正在運(yùn)行的文件,替換為編譯后的二進(jìn)制文件.對于一些簡單且經(jīng)常需要變動的業(yè)務(wù)邏輯,設(shè)計(jì)人員可以使用腳本語言來實(shí)現(xiàn)邏輯功能,這樣只需重新加載腳本文件即可實(shí)現(xiàn)服務(wù)器熱更新,從而減少維護(hù)次數(shù),帶來更大效益.目前較為熱門的腳本語言有Lua、JavaScript等,功能都比較強(qiáng)大,但也有明顯的缺點(diǎn),例如Lua實(shí)現(xiàn)面向?qū)ο蟊容^麻煩,而JavaScript和C++相互調(diào)用非常困難[3-7].本研究討論了如何利用編譯原理的相關(guān)知識,設(shè)計(jì)并實(shí)現(xiàn)一款可嵌入C++且支持面向?qū)ο笳Z法的腳本語言,該語言比Lua、JavaScript更貼近于C++,易學(xué)習(xí),功能強(qiáng)大,能夠?qū)崿F(xiàn)C++項(xiàng)目熱更新,可以為C++項(xiàng)目的開發(fā)和維護(hù)減少不必要的工作量.

        1 語言設(shè)計(jì)及語法分析

        1.1 語言設(shè)計(jì)

        語言設(shè)計(jì)中,語法的復(fù)雜程度是一個關(guān)鍵點(diǎn).如果語法很少,語言的功能則會受到影響,這會讓程序員在實(shí)現(xiàn)某些功能時消耗更多精力,而如果語法很多且復(fù)雜,就會增加解釋器設(shè)計(jì)的復(fù)雜度,同時可能影響語言的運(yùn)行效率.在本研究中,腳本語言的設(shè)計(jì)遵守以下原則:變量使用弱類型,以簡化字符串處理(字符串處理是很多業(yè)務(wù)邏輯的關(guān)鍵點(diǎn));面向?qū)ο?,方便對?shù)據(jù)進(jìn)行抽象,也方便代碼復(fù)用;支持自動垃圾回收,避免手動管理內(nèi)存;支持閉包,這對腳本語言是非常重要的功能;其他語法盡量接近C++,以減少學(xué)習(xí)成本;能很方便實(shí)現(xiàn)與C++的相互調(diào)用;盡量不引入多余且用處不大的語法.

        1.2 語法介紹

        本研究依據(jù)“1.1”項(xiàng)下的設(shè)計(jì)原則設(shè)計(jì)出了Rose腳本語言.Rose語言被設(shè)計(jì)為弱類型語言,語法本身非常簡單且與C++高度相似,此處僅簡單介紹與C++的不同之處:

        1)變量聲明.類似于Lua語言,直接聲明標(biāo)識符即可,不需要也不能加類型,變量命名規(guī)則與C++完全相同.

        2)函數(shù)及運(yùn)算符重載.為了方便解釋器的開發(fā),Rose語言不支持函數(shù)重載,也不支持運(yùn)算符重載.

        3)參數(shù)傳遞語義.C++中,參數(shù)傳遞有引用傳遞、值傳遞與地址傳遞3種(事實(shí)上值傳遞和地址傳遞可以認(rèn)為是同一種),Rose語言只有引用傳遞.

        4)位運(yùn)算.由于Rose語言的變量設(shè)計(jì)較為特殊,所以不支持位運(yùn)算.

        5)部分運(yùn)算符語義的調(diào)整.在C++中,〉〉和〈〈是2種用于位運(yùn)算的操作符, 而Rose語言不支持位運(yùn)算.此外,Rose語言中,形如“a=b;”這樣的語句,會被理解為a和b實(shí)際指向同個底層變量,而不是將b值復(fù)制給a.運(yùn)算符〉〉和〈〈在Rose語言中正好可以作為復(fù)制運(yùn)算符,例如,“a〉〉b;”表示將a值復(fù)制給b,而“a〈〈b;”則相反.

        6)模板.模板本身不適合腳本語言,因此Rose語言不支持模板.

        7)函數(shù)參數(shù).Rose語言中任何函數(shù)都默認(rèn)可以接收任意個參數(shù),函數(shù)中通過argNum關(guān)鍵字獲取當(dāng)前調(diào)用的參數(shù)個數(shù),而通過args[i]的方式獲取第i個參數(shù).

        8)函數(shù)返回值.C++中的函數(shù)返回值要么為void,要么只有一個,而Rose語言可以有任意多個返回值.

        Rose語言繼承了C++大多數(shù)優(yōu)秀的語法,屏蔽了其中不適合腳本語言且相對復(fù)雜的語法.理論上,Rose語言仍然是圖靈完備的編程語言,能做到以相對精簡的語法實(shí)現(xiàn)各種業(yè)務(wù)邏輯.

        1.3 詞法分析器設(shè)計(jì)

        詞法分析指的是讀取源代碼,并逐個掃描其中的字符,并將這些字符轉(zhuǎn)換為一系列有意義的單詞(Token).Rose語言中的Token一共有標(biāo)識符、運(yùn)算符與字面量3大類,其中字面量又分為字符串字面量和數(shù)值字面量2種.對于語法復(fù)雜的語言(如C++),其詞法分析器可以使用lex工具來實(shí)現(xiàn),但是Rose語言的語法遠(yuǎn)比C++簡單,即使引入了面向?qū)ο蟮奶匦裕訰ose語言采用手工實(shí)現(xiàn)詞法分析器.

        若使用手工實(shí)現(xiàn)詞法分析器,則需要理解有窮自動機(jī)的工作機(jī)制.圖1展示了一個用于識別不同Token的自動機(jī).

        圖1一種有窮自動機(jī)

        Token簽名如下:

        class Token

        {

        public:

        Token();

        Token(Token &&t);

        bool isIntLiteral()const;

        bool isRealLiteral()const;

        bool isStringLiteral()const;

        bool isId()const;

        bool isKeyWord()const;

        std::string toString()const;

        };

        詞法分析器設(shè)計(jì)為:

        class Lexer

        {

        Lexer();

        ~Lexer();

        void doFile(const std::string &fileName);

        void doString(const std::string &code);

        Token read();

        Token peek(int i);

        }

        其中,doFile和doString用于處理源代碼,read則用于獲取Token,peek用于預(yù)讀Token.

        函數(shù)doFile是整個Lexer的核心,其實(shí)現(xiàn)并不復(fù)雜.偽碼如下:

        while (true)

        {

        char temp = peekChar();

        if (temp == -1)

        break;

        if (isNumber(temp))

        getNumber();/*獲取數(shù)字字面量*/

        else if (isIdStart(temp))

        getId();/*獲取標(biāo)識符*/

        else

        …/*其他操作*/

        }

        函數(shù)getId、getNumber等是Lexer的私有函數(shù),用于生成一個Id類或者數(shù)值類的Token.

        1.4 語法分析器的設(shè)計(jì)

        如果使用lex實(shí)現(xiàn)詞法分析器,那么語法分析器可以使用yacc實(shí)現(xiàn).由于本研究沒有使用lex,所以語法分析也采用手工實(shí)現(xiàn).

        先設(shè)計(jì)用于表示表達(dá)式與語句等的類,由于其種類太多,所以需要設(shè)計(jì)一個抽象基類,具體為:

        class SyntaxTree

        {

        SyntaxTree();

        ~SyntaxTree();

        virtual eval()=0;

        }

        這個抽象基類是一切語法樹的共同基類,其中,eval函數(shù)是Rose語言能運(yùn)行的關(guān)鍵,作用是對當(dāng)前語法樹求值.事實(shí)上,一切語言的運(yùn)行過程,本質(zhì)上都是求值.語法分析器的設(shè)計(jì)如下:

        class Parser

        {

        Parser();

        ~Parser();

        void doFile(const std::string &file);

        void doString(const std::string &code);

        SyntaxTree getFactor();

        SyntaxTree getExpr();

        SyntaxTree getState();

        }

        和詞法分析器類似,語法分析器依然可以處理文件或是字符串.函數(shù)getFactor、getExpr、getState等用于分析不同類型的語句,其中,factor指語句中的最小因子(比如一個id,也可以是由括號括起來的表達(dá)式),expr指由若干個運(yùn)算符拼接而成的若干個因子,state指一條語句(即以分號結(jié)尾的一個expr,或if、while等語句).SyntaxTree類型遠(yuǎn)遠(yuǎn)超過這3種,任何一種運(yùn)算符都有對應(yīng)的SyntaxTree類.

        語法分析使用LL(K),這種方式最適合手工實(shí)現(xiàn),缺點(diǎn)是效率可能會降低,不過只影響編譯效率,不會影響運(yùn)行效率.

        Rose語言的核心是運(yùn)算符與表達(dá)式,以下代碼展示了getExpr的工作原理:

        SyntaxTree Parser::getExpr()

        {

        SyntaxTree left = getFactor();

        if (left.get() == nullptr)

        return left;

        while (true)

        {

        OperatorValue *op = findOperator(data->

        token.peek(0).getString());

        if (op == nullptr)

        break;

        left = shift(op,std::move(left));

        }

        return left;

        }

        其原理是:首先獲取一個因子,然后獲取一個雙目運(yùn)算符.如果沒有獲取到,則直接返回,否則調(diào)用shift函數(shù)進(jìn)行調(diào)整(因?yàn)檫\(yùn)算符存在優(yōu)先級,所以需要調(diào)整).函數(shù)shift的實(shí)現(xiàn)如下:

        SyntaxTree Parser::doShift(OperatorValue *op, SyntaxTree left)

        {

        SyntaxTree right = getPrimary();

        while (true)

        {

        OperatorValue *op1 = findOperator(t,comma);

        if (op1&&isExpr(op, op1))

        {

        data->token.read();

        right = doShift(op1,std::move(right));

        }

        else

        break;

        }

        return op->make(std::move(left),std::move(right));

        }

        其中,isExpr用于判斷2個運(yùn)算符的優(yōu)先級.如果存在優(yōu)先級差,則遞歸進(jìn)行調(diào)整,這樣能保證生成的語法樹是正確的.而make是OperatorValue類的成員函數(shù),用于根據(jù)不同的運(yùn)算符生成不同的語法樹.

        圖2展示了表達(dá)式1*(2+3)-5經(jīng)過以上語法分析后產(chǎn)生的語法樹.

        圖2語法樹

        2 虛擬機(jī)及相關(guān)設(shè)計(jì)

        2.1 虛擬機(jī)設(shè)計(jì)

        虛擬機(jī)的基本設(shè)計(jì)如下:

        class VirtualMachine

        {

        void setFunctions(std::vector functions);

        bool run();

        void doFile(const std::string &file);

        Object getResult();

        }

        其中,F(xiàn)unction是一個類,是若干個語句的集合.由于Rose語言沒有g(shù)oto語句,所以通常情況下,這些語句是順序執(zhí)行的(if及for等也被視為語句).

        首先,通過setFunctions來為虛擬機(jī)添加函數(shù)(Rose語言的設(shè)計(jì)是基于函數(shù)的),這個函數(shù)不需要用戶調(diào)用,而是由Parser調(diào)用.函數(shù)run用于執(zhí)行腳本,執(zhí)行的入口為函數(shù)main,如果沒有函數(shù)main,則run不能執(zhí)行成功.腳本執(zhí)行的原理相對簡單,依次調(diào)用每條語句的eval函數(shù)即可,它們會自動遞歸調(diào)用下屬語句.函數(shù)doFile用于加載文件,由Parser具體進(jìn)行解析.函數(shù)getResult則用于在腳本執(zhí)行完畢之后獲取運(yùn)行的結(jié)果,其中,Object是表示變量的類.只有虛擬機(jī)并不足以完美運(yùn)行腳本,而該虛擬機(jī)還缺少2個部分,即Object和儲存Object的容器.Object的設(shè)計(jì)如下:

        class Object

        {

        bool isNum();

        bool isString();

        bool isTable();

        void *data;

        }

        Rose語言本身是弱類型的.一個Object可以是數(shù)字、字符串或者表,甚至可以是函數(shù)(具體實(shí)現(xiàn)為函數(shù)指針),其中字符串和函數(shù)最簡單,而數(shù)字則會根據(jù)實(shí)際情況選擇使用double或者大數(shù)類來表示.表可以是數(shù)組,也可以是哈希表,也可以是對象,通過[]或者.運(yùn)算符可以訪問成員,例如a[1]、a.foo()等.

        用于儲存Object的容器設(shè)計(jì)也很簡單,設(shè)計(jì)成鏈表的方式即可(考慮到垃圾回收機(jī)制).

        2.2 垃圾回收算法設(shè)計(jì)

        變量不能像C++那樣離開作用域后馬上被析構(gòu),這樣就帶來一個問題,即如何進(jìn)行垃圾回收.本研究設(shè)計(jì)的垃圾回收算法如下:

        1)從最頂層函數(shù)調(diào)用(通常是main,但也可以是其他)開始一直到當(dāng)前調(diào)用,將其中的局部Object進(jìn)行標(biāo)記.每標(biāo)記一個Object,都遞歸標(biāo)記它的成員(如果這個Object是一個對象或表而非字符串或數(shù)值).遞歸中若遇到已經(jīng)標(biāo)記的Object則不再往下遞歸(避免死循環(huán)).

        2)掃描儲存所有Object的鏈表,移除并釋放其中未被標(biāo)記的Object.這些Object是應(yīng)該被回收的,因?yàn)闊o法通過任何方式訪問到它們.

        3)重新掃描2)中的鏈表,并將所有Object的標(biāo)記取消.

        Rose語言的垃圾回收默認(rèn)是自動的,即每進(jìn)行一定次數(shù)的函數(shù)調(diào)用,便會進(jìn)行垃圾回收.用戶也可以設(shè)置為手動回收,使用System.gc()進(jìn)行.

        3 腳本語言與C++的通信

        3.1 C++調(diào)用Rose

        C++調(diào)用Rose的方法相對簡單.由于虛擬機(jī)的run函數(shù)默認(rèn)是以main為入口,只需要添加一個用于調(diào)用任意函數(shù)的函數(shù):

        bool call(const std::string&name,const std::vector &args);

        其中,name是函數(shù)名,args是參數(shù).

        例如,一段C++代碼:

        VirtualMachine vm;

        vm.doFile(″test.rose″);

        vm.call(″foo″,std::vector{Object(″Hello World″)})

        其中,foo函數(shù)以Rose語言的形式實(shí)現(xiàn),代碼如下:

        foo()

        {

        System.print(args[0]);

        }

        程序運(yùn)行后,在標(biāo)準(zhǔn)輸出通道中可以讀取到以下內(nèi)容:

        Hello World

        如果foo有返回值,則可以通過vm.getReturnValues()獲取返回值,獲取到的結(jié)果為std::vector類型.

        此外,還可以通過以下函數(shù)獲取腳本語言中的任意全局變量:

        Object getGlobal(const std::string&name);

        例如:

        std:cout<

        其中,value是定義于Rose文件中的一個全局變量:

        value=″test″;

        3.2 Rose調(diào)用C++

        Rose對C++的調(diào)用相對更復(fù)雜一些,需要先通過虛擬機(jī)的一個成員函數(shù)進(jìn)行注冊:

        void register(const std::string &name,int (*fun)(VirtualMachine *));

        其中,name是Rose調(diào)用的函數(shù)名,fun是一個函數(shù)指針,指向被注冊的函數(shù).當(dāng)Rose語言中調(diào)用名為name的函數(shù)時,實(shí)際會調(diào)用fun函數(shù),在fun函數(shù)中通過VirtualMachine指針來獲取Rose語言傳遞過來的參數(shù),并處理相關(guān)業(yè)務(wù)邏輯.而fun的返回值代表了返回給Rose語言的值的個數(shù).

        例如,以下函數(shù)是用于計(jì)算若干個參數(shù)的平方和:

        int square(VirtualMachine *vm)

        {

        std::vector args=vm->getArgs();

        std::vector result;

        for(Object &t:args)

        result.push-back(Object(pow(t.toInt(),2)));

        vm->setResult(result);

        return result.size();

        }

        注冊方式為:

        vm.register(″square″,square);

        Rose語言中的調(diào)用方式為:

        foo()

        {

        a,b=square(1,2);/*多返回值*/

        print(a+b);

        }

        運(yùn)行結(jié)果為:5

        4 結(jié) 語

        本研究設(shè)計(jì)并實(shí)現(xiàn)了一款可嵌入C++的腳本語言,同時為該語言實(shí)現(xiàn)了一虛擬機(jī),使其能夠很方便地調(diào)用C++或被C++調(diào)用,并且該虛擬機(jī)也支持自動垃圾回收.這樣的腳本語言能夠讓C++項(xiàng)目變得更容易維護(hù),具有現(xiàn)實(shí)的應(yīng)用意義.更重要的是,它為用戶提供了一種思路去設(shè)計(jì)和創(chuàng)造編程語言及開發(fā)工具,其能夠在特定的場景和領(lǐng)域中發(fā)揮積極的意義.

        猜你喜歡
        腳本語言運(yùn)算符詞法
        詞法 名詞、代詞和冠詞
        老祖?zhèn)魇诨具\(yùn)算符
        一種面向SSC的電信增值業(yè)務(wù)的生成方法及實(shí)現(xiàn)
        應(yīng)用于詞法分析器的算法分析優(yōu)化
        基于Unity3D的坦克大戰(zhàn)游戲設(shè)計(jì)與實(shí)現(xiàn)
        談對外漢語“詞法詞”教學(xué)
        淺析計(jì)算機(jī)技術(shù)在flash動畫中的應(yīng)用
        基于SiPESC平臺的Python擴(kuò)展模塊開發(fā)
        C++運(yùn)算符重載剖析
        價值工程(2014年17期)2014-04-16 03:29:20
        表達(dá)式求值及符號推導(dǎo)
        亚洲色图偷拍自拍亚洲色图| 女人色毛片女人色毛片18| 亚洲午夜精品久久久久久人妖| 国产成人aa在线观看视频| 国产理论亚洲天堂av| 免费a级毛片18禁网站免费| a级毛片高清免费视频就| 91天堂素人精品系列全集亚洲 | 亚洲尺码电影av久久| 特级无码毛片免费视频尤物| 色翁荡息又大又硬又粗又视频图片 | 绝顶高潮合集videos| 亚洲精品aa片在线观看国产| 国产亚洲日韩欧美一区二区三区| 黄色大片一区二区中文字幕| 特级黄色大片性久久久| 天天躁夜夜躁狠狠是什么心态 | 99在线精品免费视频| www国产亚洲精品久久网站| 狠狠色狠狠色综合网老熟女| 中文字幕视频一区二区| 中国娇小与黑人巨大交| 久久99久久99精品免观看| 日韩中文字幕精品免费一区| 一区二区三区日韩蜜桃| 国产福利永久在线视频无毒不卡| 亚洲色欲久久久综合网| 欧美久久久久中文字幕| 激情偷拍视频一区二区| www夜插内射视频网站| 午夜成人精品福利网站在线观看 | 久久青草亚洲AV无码麻豆| 91精品国产自拍视频| 人人人妻人人澡人人爽欧美一区| 无限看片在线版免费视频大全| 国产成人午夜av影院| av免费播放网站在线| 亚洲精品午睡沙发系列| 亚洲午夜看片无码| 亚洲国产不卡免费视频| 亚洲精品国产第一综合色吧|