亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

python解釋器spython使用及原理解析

系統(tǒng) 1985 0

簡介

出于個(gè)人愛好和某種需求,我再16年對python的解釋器產(chǎn)生了濃厚興趣,并且下定決心重新實(shí)現(xiàn)一個(gè)版本。我個(gè)人再游戲服務(wù)器開發(fā)中,對c++嵌入lua和python都有著豐富應(yīng)用經(jīng)驗(yàn),自認(rèn)為對二者的優(yōu)劣有著深刻的理解。

python針對lua的最大優(yōu)勢是python是完備的程序語言,類、模塊包括豐富的庫和方便好用的字符串操作,可以說python用來實(shí)現(xiàn)功能會(huì)優(yōu)雅很多,而lua最大的優(yōu)勢就是小巧高效,另外lua的lua_state是可以有多個(gè)實(shí)例的,這樣就可以多線程使用lua(一個(gè)線程單獨(dú)一個(gè)lua_state),而python解釋器因?yàn)橛腥纸忉屍麈i,所以無法實(shí)現(xiàn)多python解釋器實(shí)例。

考慮到在嵌入python的應(yīng)用場景中,所用到python的功能都是比較簡單通用的功能,比如類、模塊,函數(shù),一些復(fù)雜的類庫也不常用,所以我就想實(shí)現(xiàn)一個(gè)不使用全局解釋器鎖,可以有多個(gè)python解釋器鎖的解釋器。所以16年底,我自己實(shí)現(xiàn)了一下python解釋器第一版,第一版是使用AST虛擬語法樹直接解析的,雖然做了必要的優(yōu)化,但是性能。。。。仍然不忍直視。

平常我一直吐槽python跑的沒有l(wèi)ua快,但是吐槽是一碼事,自己實(shí)現(xiàn)真的就是另一碼事了。我仔細(xì)分析了第一版性能低的原因是選錯(cuò)了路!python的虛擬機(jī)是講語法樹翻譯成ByteCode,然后有個(gè)Virtual Machine不斷的解釋bytecode,而vm的運(yùn)行又分堆棧模式和寄存器模式,python就是堆棧模式的,而lua是寄存器模式的,寄存器模式是現(xiàn)在的趨勢,這也是lua跑到更快的重要原因。我的第一版VM用AST直接跑,選錯(cuò)了路,無論如何也太快不了。

但是我仍然把這個(gè)第一版打了個(gè)分支,分享出來,因?yàn)楫?dāng)我實(shí)現(xiàn)用寄存器模式的VM的時(shí)候,感覺無論如何也無法設(shè)計(jì)的像AST直接解析的VM那樣優(yōu)雅、直接。AST直接解析的方式真的太直觀了,雖然效率很低,但是其仍然有很大的應(yīng)用價(jià)值。

比如protocolbuff、thrift這些通過定義語法文件生成代碼的這類工具,對語法解析的效率要求不高,那么這個(gè)版本的VM再這些領(lǐng)域還是有很大的參考價(jià)值。

內(nèi)部實(shí)現(xiàn)層次:

python解釋器spython使用及原理解析_第1張圖片

Python BNF

一提到實(shí)現(xiàn)腳本解釋器,估計(jì)很多人都會(huì)撓頭,不知道從何入手。剛開始我也是這樣,我把大學(xué)里的編譯原理從床底下一堆打入冷宮的數(shù)量翻出來,一頓猛看。但是仍然沒有找到很大頭緒,后來我就在python.org上一頓逛,也下載了python的源碼分析,源碼目錄有python的BNF描述文件,因?yàn)槲乙呀?jīng)看過一遍編譯原理了,BNF就看的很懂,從頭到尾讀了一遍了以后,靈光乍現(xiàn)啊!BNF就是完整的解析python語法的流程說明啊!截取一小段做個(gè)說明:

            
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
try_stmt: ('try' ':' suite
     ((except_clause ':' suite)+
     ['else' ':' suite]
     ['finally' ':' suite] |
     'finally' ':' suite))
with_stmt: 'with' with_item (',' with_item)* ':' suite
with_item: test ['as' expr]
# NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test [('as' | ',') test]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
          

簡單解釋下,python的Grammar BNF是從頂之下遞歸描述的。上面最上邊定義的是compound_stmt復(fù)雜語句,而compound_stmt有if、while、for、try、with、函數(shù)定義、類定義、修飾器定義幾種,下面緊接著定義了if語句if_stmt的語法規(guī)則,這樣在c++實(shí)現(xiàn)解析python語法的時(shí)候,就可以從頂向下按照這個(gè)BNF嘗試解析,如果不滿足這個(gè)BNF語法要求的就報(bào)錯(cuò)。我為了生成跟這個(gè)BNF一致的代碼結(jié)構(gòu),寫了個(gè)python腳本解析這個(gè)BNF自動(dòng)生成C++的解析函數(shù)。生成的C++代碼示例如下:

            
class Parser{
public:
 ExprASTPtr parse(Scanner& scanner);

 //! single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
 ExprASTPtr parse_single_input();
 //! file_input: (NEWLINE | stmt)* ENDMARKER
 ExprASTPtr parse_file_input();
 //! eval_input: testlist NEWLINE* ENDMARKER
 ExprASTPtr parse_eval_input();
 //! decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
 ExprASTPtr parse_decorator();
 //! decorators: decorator+
 ExprASTPtr parse_decorators();
 //! decorated: decorators (classdef | funcdef)
 ExprASTPtr parse_decorated();
 //! funcdef: 'def' NAME parameters ':' suite
 ExprASTPtr parse_funcdef();
 //! parameters: '(' [varargslist] ')'
 ExprASTPtr parse_parameters();
 //! varargslist: ((fpdef ['=' test] ',')*
 //!        fpdef ['=' test] (',' fpdef ['=' test])* [','])
 ExprASTPtr parse_varargslist();
 //! fpdef: NAME | '(' fplist ')'
 ExprASTPtr parse_fpdef();
 //! fplist: fpdef (',' fpdef)* [',']
 ExprASTPtr parse_fplist();
 //! stmt: simple_stmt | compound_stmt
 ExprASTPtr parse_stmt();
 //! simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
 ExprASTPtr parse_simple_stmt();
 //! small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
 //!       import_stmt | global_stmt | exec_stmt | assert_stmt)
 ExprASTPtr parse_small_stmt();
 //! expr_stmt: testlist (augassign (yield_expr|testlist) |
 ExprASTPtr parse_expr_stmt();
.................................
          

Scanner的實(shí)現(xiàn)

scanner負(fù)責(zé)解析python代碼,把python代碼分隔這一個(gè)個(gè)Token對象,并且Token對象的定義如下:

            
struct Token{
 Token():nTokenType(0), nVal(0), fVal(0.0), nLine(0){
 }

 std::string dump() const;
 int       nTokenType;
 int64_t     nVal;
 double     fVal;
 std::string   strVal;
 int       nLine;
};

enum ETokenType {
  TOK_EOF = 0,
  //TOK_DEF = -2,
  TOK_VAR = -4,
  TOK_INT = -5,
  TOK_FLOAT = -6,
  TOK_STR = -7,
  TOK_CHAR = -8,
};
          

nTokenType定義為ETokenType的枚舉。Scanner只掃描python代碼,而不解析語法,所有的python代碼都會(huì)解析成要么整數(shù),要么浮點(diǎn)數(shù)要么字符串。這個(gè)跟原生的python是有區(qū)別的,原生python的數(shù)字對象可以表達(dá)任意數(shù)字,但是為了實(shí)現(xiàn)簡便,做了簡化處理,這也是參考了lua的實(shí)現(xiàn)方式每token對象會(huì)記錄所屬的行號,方便語法報(bào)錯(cuò)提供有用的信息。

具體scanner的實(shí)現(xiàn)就不貼出來了,感興趣的可以去查看源碼,還是比較簡單的。

Parser的實(shí)現(xiàn)

Parser的頭文件是腳本解析BNF自動(dòng)生成的。負(fù)責(zé)把scanner解析的token列表,按照BNF的規(guī)則構(gòu)造成AST。AST節(jié)點(diǎn)對象定義為ExprAST:

            
class ExprAST {
public:
  ExprAST(){
  }
  virtual ~ExprAST() {}
  virtual PyObjPtr& eval(PyContext& context) = 0;

  unsigned int getFieldIndex(PyContext& context, PyObjPtr& obj);

  virtual PyObjPtr& getFieldVal(PyContext& context);
  virtual PyObjPtr& assignVal(PyContext& context, PyObjPtr& v){
    PyObjPtr& lval = this->eval(context);
    lval = v;
    return lval;
  }
  virtual void delVal(PyContext& context){
    PyObjPtr& lval = this->eval(context);
    lval = NULL;
  }

  virtual int getType() {
    return 0;
  }

public:
  std::string name;
  ExprLine  lineInfo;
  //std::vector
            
              
                 > module2objcet2fieldIndex;
  std::vector
                
                           module2objcet2fieldIndex;
};
class PyObj {
public:
  RefCounterData* getRefData(){
    return &refdata;
  }
  void release();
  typedef PySmartPtr
                  
                     PyObjPtr;
  PyObj():m_pObjIdInfo(NULL), handler(NULL){}
  virtual ~PyObj() {}

  int getType() const;
  virtual int getFieldNum() const { return m_objStack.size(); }
  static std::string dump(PyContext& context, PyObjPtr& self, int preBlank = 0);

  virtual PyObjPtr& getVar(PyContext& c, PyObjPtr& self, ExprAST* e);
  virtual const ObjIdInfo& getObjIdInfo() = 0;

  void clear(){
    m_objStack.clear();
  }
  inline PyObjHandler* getHandler() { return handler; }
  inline const PyObjHandler* getHandler() const { return handler; }
public:
  std::vector
                    
                        m_objStack;
  ObjIdInfo*        m_pObjIdInfo;
  PyObjHandler*      handler;
  RefCounterData      refdata;
};
typedef PyObj::PyObjPtr PyObjPtr;
                    
                  
                
              
            
          

ExprAST抽象了AST節(jié)點(diǎn)的幾個(gè)操作。最主要的就是求值操作eval。比如100求值就是100,'abc'求值就是字符串'abc',生成對應(yīng)的值對象。每個(gè)值對象都繼承PyObj。每個(gè)PyObj都會(huì)定義ObjHander接口用于實(shí)現(xiàn)python對象的各個(gè)操作,比如+、-、/等,不同的python值對象,響應(yīng)的操作是不一樣,這里利用了c++的多態(tài)。

            
class PyObjHandler{
public:
 virtual ~PyObjHandler(){}

 virtual int getType() const = 0;

 virtual std::string handleStr(PyContext& context, const PyObjPtr& self) const;
 virtual std::string handleRepr(PyContext& context, const PyObjPtr& self) const;
 virtual int handleCmp(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;
 virtual bool handleBool(PyContext& context, const PyObjPtr& self) const;
 virtual bool handleEqual(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;
 virtual bool handleLessEqual(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;
 virtual bool handleGreatEqual(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;
 virtual bool handleContains(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;

 virtual bool handleLess(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;
 virtual bool handleGreat(PyContext& context, const PyObjPtr& self, const PyObjPtr& val) const;

 virtual PyObjPtr& handleAdd(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleSub(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleMul(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleDiv(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleMod(PyContext& context, PyObjPtr& self, PyObjPtr& val);


 virtual PyObjPtr& handleIAdd(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleISub(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleIMul(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleIDiv(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual PyObjPtr& handleIMod(PyContext& context, PyObjPtr& self, PyObjPtr& val);

 virtual PyObjPtr& handleCall(PyContext& context, PyObjPtr& self, std::vector
            
              & allArgsVal,
                std::vector
              
                & argAssignVal);
 virtual size_t  handleHash(PyContext& context, const PyObjPtr& self) const;
 virtual bool handleIsInstance(PyContext& context, PyObjPtr& self, PyObjPtr& val);
 virtual long handleLen(PyContext& context, PyObjPtr& self);
 virtual PyObjPtr& handleSlice(PyContext& context, PyObjPtr& self, PyObjPtr& startVal, int* stop, int step);
 virtual PyObjPtr& handleSliceAssign(PyContext& context, PyObjPtr& self, PyObjPtr& k, PyObjPtr& v);
 virtual void handleSliceDel(PyContext& context, PyObjPtr& self, PyObjPtr& k){}

 virtual void handleRelese(PyObj* data);
};
              
            
          

Python庫的實(shí)現(xiàn)

實(shí)現(xiàn)的python庫列表如下:

  • list dict tuple copy string
  • datetime
  • json
  • math
  • os
  • random
  • open stringio
  • struct
  • sys
  • weak

總結(jié)

spython就是small python,本來想實(shí)現(xiàn)最簡版本的python解釋器,后來實(shí)現(xiàn)的比較順,一口氣把常用的python庫都實(shí)現(xiàn)了。spython最成功的部分就是ast的解析和執(zhí)行,代碼結(jié)構(gòu)清晰完全按照bnf的流程來,很直接明了。

缺點(diǎn)主要有二。一是語法報(bào)錯(cuò)還是太簡陋,不夠友好。二是性能達(dá)不到原生python的性能。前文已經(jīng)說過了,要達(dá)到甚至超過原生python的水平,必須要實(shí)現(xiàn)基于寄存器的VM,這個(gè)已經(jīng)著手再弄了,暫時(shí)還不會(huì)放出代碼,等差不多成型了再放出來吧。

代碼地址:https://git.oschina.net/ownit/spython

構(gòu)建:Linux下直接make就可以了,win下需要用dev c++

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 毛片毛片毛片毛片毛片 | 伊人精品综合 | 香蕉国产人午夜视频在线观看 | 国产精品成人久久久久久久 | 久久一区视频 | 国产国产人免费人成成免视频 | 久久亚洲精品一区二区三区浴池 | 夜夜女人国产香蕉久久精品 | 国产欧美国产精品第一区 | 在线精品国精品国产不卡 | 欧美胖老妇 | 久免费视频 | 亚洲国产美女精品久久久久 | 精品视频免费 | 日本欧美精品 | 夜夜操天天射 | 国内外一级毛片 | 奇米在线777在线视频 | 亚洲 欧美 卡通 在线 另类 | 天天干天天干天天天天天天爽 | 奇米在线观看 | 国产日韩久久久精品影院首页 | 久久狠狠色狠狠色综合 | 欧美日韩久久 | 福利网站在线观看 | 亚洲精品女同中文字幕在线 | 久久久久久91 | 四虎澳门永久8848在线影院 | 2019年国产高清情侣视频 | 国产成人精品亚洲2020 | 国产精品热久久毛片 | 在线性爱视频 | 国产精品久久久久影院免费 | 国产精品入口麻豆午夜 | 美日韩在线视频 | 91网站国产 | 免费黄色毛片视频 | 久久国产精品久久久久久久久久 | 四虎国产精品一区二区 | 国产亚洲精品成人久久网站 | 国产欧美综合在线一区二区三区 |