• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 17  文章 - 48  trackbacks - 0
            <2013年11月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            1234567

            常用鏈接

            留言簿(3)

            隨筆檔案

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

                 摘要: lua的VM執(zhí)行代碼是從lvm.c中的void luaV_execute(lua_State *L)開始:Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->void luaV_execute (lua_State *L)&n...  閱讀全文
            posted @ 2012-09-19 12:34 airtrack 閱讀(7197) | 評(píng)論 (5)編輯 收藏

            C的NULL

            在C語言中,我們使用NULL表示空指針,也就是我們可以寫如下代碼:

             

            int *i = NULL;
            foo_t *f = NULL;

             

            實(shí)際上在C語言中,NULL通常被定義為如下:

             

            #define NULL ((void *)0)

             

            也就是說NULL實(shí)際上是一個(gè)void *的指針,然后吧void *指針賦值給int *和foo_t *的指針的時(shí)候,隱式轉(zhuǎn)換成相應(yīng)的類型。而如果換做一個(gè)C++編譯器來編譯的話是要出錯(cuò)的,因?yàn)镃++是強(qiáng)類型的,void *是不能隱式轉(zhuǎn)換成其他指針類型的,所以通常情況下,編譯器提供的頭文件會(huì)這樣定義NULL:

             

            #ifdef __cplusplus
            #define NULL 0
            #else
            #define NULL ((void *)0)
            #endif

             


            C++的0

            因?yàn)镃++中不能將void *類型的指針隱式轉(zhuǎn)換成其他指針類型,而又為了解決空指針的問題,所以C++中引入0來表示空指針,這樣就有了類似上面的代碼來定義NULL。實(shí)際上C++的書都會(huì)推薦說C++中更習(xí)慣使用0來表示空指針而不是NULL,盡管NULL在C++編譯器下就是0。為什么C++的書都推薦使用0而不是NULL來表示空指針呢?我們看一個(gè)例子:

            在foo.h文件中聲明了一個(gè)函數(shù):

             

            void bar(sometype1 a, sometype2 *b);

             

            這個(gè)函數(shù)在a.cpp、b.cpp中調(diào)用了,分別是:

            a.cpp:

             


            bar(a, b);

             

            b.cpp:

             


            bar(a, 0);

             

            好的,這些代碼都是正常完美的編譯運(yùn)行。但是突然在某個(gè)時(shí)候我們功能擴(kuò)展,需要對(duì)bar函數(shù)進(jìn)行擴(kuò)展,我們使用了重載,現(xiàn)在foo.h的聲明如下:

             

            void bar(sometype1 a, sometype2 *b);
            void bar(sometype1 a, int i);

             

            這個(gè)時(shí)候危險(xiǎn)了,a.cpp和b.cpp中的調(diào)用代碼這個(gè)時(shí)候就不能按照期望的運(yùn)行了。但是我們很快就會(huì)發(fā)現(xiàn)b.cpp中的0是整數(shù),也就是在overload resolution的時(shí)候,我們知道它調(diào)用的是void bar(sometype1 a, int i)這個(gè)重載函數(shù),于是我們可以做出如下修改讓代碼按照期望運(yùn)行:

             

            bar(a, static_cast<sometype2 *>(0));

             

            我知道,如果我們一開始就有bar的這兩個(gè)重載函數(shù)的話,我們會(huì)在一開始就想辦法避免這個(gè)問題(不使用重載)或者我們寫出正確的調(diào)用代碼,然而后面的這個(gè)重載函數(shù)或許是我們幾個(gè)月或者很長(zhǎng)一段時(shí)間后加上的話,那我們出錯(cuò)的可能性就會(huì)加大了不少。貌似我們現(xiàn)在說道的這些跟C++通常使用0來表示空指針沒什么關(guān)系,好吧,假設(shè)我們的調(diào)用代碼是這樣的:

            foo.h

             

            void bar(sometype1 a, sometype2 *b);

             

            a.cpp

             


            bar(a, b);

             

            b.cpp

             


            bar(a, NULL);

             

            當(dāng)bar的重載函數(shù)在后面加上來了之后,我們會(huì)發(fā)現(xiàn)出錯(cuò)了,但是出錯(cuò)的時(shí)候,我們找到b.cpp中的調(diào)用代碼也很快可能忽略過去了,因?yàn)槲覀冇玫氖荖ULL空指針啊,應(yīng)該是調(diào)用的void bar(sometype1 a, sometype2 *b)這個(gè)重載函數(shù)啊。實(shí)際上NULL在C++中就是0,寫NULL這個(gè)反而會(huì)讓你沒那么警覺,因?yàn)镹ULL不夠“明顯”,而這里如果是使用0來表示空指針,那就會(huì)夠“明顯”,因?yàn)?是空指針,它更是一個(gè)整形常量。

            在C++中,使用0來做為空指針會(huì)比使用NULL來做空指針會(huì)讓你更加警覺。


            C++ 11的nullptr

            雖然上面我們說明了0比NULL可以讓我們更加警覺,但是我們并沒有避免這個(gè)問題。這個(gè)時(shí)候C++ 11的nullptr就很好的解決了這個(gè)問題,我們?cè)贑++ 11中使用nullptr來表示空指針,這樣最早的代碼是這樣的,

            foo.h

             

            void bar(sometype1 a, sometype2 *b);

             

            a.cpp

             


            bar(a, b);

             

            b.cpp

             


            bar(a, nullptr);

             

            在我們后來把bar的重載加上了之后,代碼是這樣:

            foo.h

             

            void bar(sometype1 a, sometype2 *b);
            void bar(sometype1 a, int i);

             

            a.cpp

             


            bar(a, b);

             

            b.cpp

             


            bar(a, nullptr);

             

            這時(shí)候,我們的代碼還是能夠如預(yù)期的一樣正確運(yùn)行。


            在沒有C++ 11的nullptr的時(shí)候,我們?cè)趺唇鉀Q避免這個(gè)問題呢?我們可以自己實(shí)現(xiàn)一個(gè)(《Imperfect C++》上面有一個(gè)實(shí)現(xiàn)):

             

            const
            class nullptr_t
            {
            public:
                template<class T>
                inline operator T*() const
                    { return 0; }

                template<class C, class T>
                inline operator T C::*() const
                    { return 0; }
             
            private:
                void operator&() const;
            } nullptr = {};

             


            雖然這個(gè)東西被大家討論過很多次了,但是我覺得還是有必要再討論一下,畢竟在C++ 11還沒有普及之前,我們還是應(yīng)該知道怎么去避免問題,怎么很快的找到問題。

            posted @ 2012-09-16 01:08 airtrack 閱讀(18108) | 評(píng)論 (3)編輯 收藏

            接上一篇:lua源碼剖析(一)

            詞法分析

              

            lua對(duì)與每一個(gè)文件(chunk)建立一個(gè)LexState來做詞法分析的context數(shù)據(jù),此結(jié)構(gòu)定義在llex.h中。詞法分析根據(jù)語法分析的需求有當(dāng)前token,有l(wèi)ookahead token,LexState結(jié)構(gòu)如圖:


            其中token結(jié)構(gòu)中用int存儲(chǔ)實(shí)際token值,此token值對(duì)于單字符token(+ - * /之類)就表示自身,對(duì)于多字符(關(guān)鍵字等)token是起始值為257的枚舉值,在llex.h文件中定義:

            #define FIRST_RESERVED     257

            /*
            * WARNING: if you change the order of this enumeration,
            * grep "ORDER RESERVED"
            */
            enum RESERVED {
              /* terminal symbols denoted by reserved words */
              TK_AND = FIRST_RESERVED, TK_BREAK,
              TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
              TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
              TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
              /* other terminal symbols */
              TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_DBCOLON, TK_EOS,
              TK_NUMBER, TK_NAME, TK_STRING
            };

            token結(jié)構(gòu)中還有一個(gè)成員seminfo,這個(gè)表示語義信息,根據(jù)token的類型,可以表示數(shù)值或者字符串。

            lex提供函數(shù)luaX_next和luaX_lookahead分別lex下一個(gè)token和lookahead token,在內(nèi)部是通過llex函數(shù)來完成詞法分析。


            語法分析


            lua語法分析是從lparser.c中的luaY_parser開始:

             

            Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
                                  Dyndata *dyd, const char *name, int firstchar) {
              LexState lexstate;
              FuncState funcstate;
              Closure *cl = luaF_newLclosure(L, 1);  /* create main closure */
              /* anchor closure (to avoid being collected) */
              setclLvalue(L, L->top, cl);
              incr_top(L);
              funcstate.f = cl->l.p = luaF_newproto(L);
              funcstate.f->source = luaS_new(L, name);  /* create and anchor TString */
              lexstate.buff = buff;
              lexstate.dyd = dyd;
              dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
              luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
              mainfunc(&lexstate, &funcstate);
              lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
              /* all scopes should be correctly finished */
              lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
              return cl;  /* it's on the stack too */
            }

             

            此函數(shù)創(chuàng)建一個(gè)closure并把LexState和FuncState初始化后調(diào)用mainfunc開始parse,其中FuncState表示parse時(shí)函數(shù)狀態(tài)信息的,如圖:


            每當(dāng)parse到一個(gè)function的時(shí)候都會(huì)建立一個(gè)FuncState結(jié)構(gòu),并將它與所嵌套的函數(shù)通過prev指針串聯(lián)起來,body函數(shù)就是完成嵌套函數(shù)parse。

             

            static void body (LexState *ls, exposed *e, int ismethod, int line) {
              /* body ->  `(' parlist `)' block END */
              FuncState new_fs;
              BlockCnt bl;
              new_fs.f = addprototype(ls);
              new_fs.f->linedefined = line;
              open_func(ls, &new_fs, &bl);
              checknext(ls, '(');
              if (ismethod) {
                new_localvarliteral(ls, "self");  /* create 'self' parameter */
                adjustlocalvars(ls, 1);
              }
              parlist(ls);
              checknext(ls, ')');
              statlist(ls);
              new_fs.f->lastlinedefined = ls->linenumber;
              check_match(ls, TK_END, TK_FUNCTION, line);
              codeclosure(ls, e);
              close_func(ls);
            }

             


            FuncState中的f指向這個(gè)函數(shù)的Proto,Proto中保存著函數(shù)的指令、變量信息、upvalue信息等其它信息,Proto的結(jié)構(gòu)如圖:


            k指向一個(gè)這個(gè)Proto中使用到的常量,code指向這個(gè)Proto的指令數(shù)組,Proto **p指向這個(gè)Proto內(nèi)部的Proto列表,locvars存儲(chǔ)local變量信息,upvalues存儲(chǔ)upvalue的信息,cache指向最后創(chuàng)建的closure,source指向這個(gè)Proto所屬的文件名,后面的size*分別表示前面各個(gè)指針指向的數(shù)組的大小,numparams表示固定的參數(shù)的個(gè)數(shù),is_vararg表示這個(gè)Proto是否是一個(gè)變參函數(shù),maxstacksize表示最大stack大小。


            FuncState中的ls指向LexState,在LexState中有一個(gè)Dyndata的結(jié)構(gòu),這個(gè)結(jié)構(gòu)用于保存在parse一個(gè)chunk的時(shí)候所存儲(chǔ)的gt label list和label list以及所有active變量列表,其中g(shù)t label list存儲(chǔ)的是未匹配的goto語句和break語句的label信息,而label list存儲(chǔ)的是已聲明的label。待出現(xiàn)一個(gè)gt label的時(shí)候就在label list中查找是否有匹配的label,若出現(xiàn)一個(gè)label也將在gt label list中查找是否有匹配的gt。


            LuaY_parser調(diào)用mainfunc開始parse一個(gè)chunk:

             

            static void mainfunc (LexState *ls, FuncState *fs) {
              BlockCnt bl;
              expdesc v;
              open_func(ls, fs, &bl);
              fs->f->is_vararg = 1;  /* main function is always vararg */
              init_exp(&v, VLOCAL, 0);  /* create and */
              newupvalue(fs, ls->envn, &v);  /* set environment upvalue */
              luaX_next(ls);  /* read first token */
              statlist(ls);  /* parse main body */
              check(ls, TK_EOS);
              close_func(ls);
            }

             


            在mainfunc中通過open_func函數(shù)完成對(duì)進(jìn)入某個(gè)函數(shù)進(jìn)行parse之前的初始化操作,每parse進(jìn)一個(gè)block的時(shí)候,將建立一個(gè)BlockCnt的結(jié)構(gòu)并與上一個(gè)BlockCnt連接起來,當(dāng)parse完一個(gè)block的時(shí)候就回彈出最后一個(gè)BlockCnt結(jié)構(gòu)。BlockCnt結(jié)構(gòu)中的其它變量的意思是:nactvar表示這個(gè)block之前的active var的個(gè)數(shù),upval表示這個(gè)block是否有upvalue被其它block訪問,isloop表示這個(gè)block是否是循環(huán)block。mainfunc中調(diào)用statlist,statlist調(diào)用statement開始parse語句和表達(dá)式。


            statement分析語句采用的是LL(2)的遞歸下降語法分析法。在statement里面通過case語句處理各個(gè)帶關(guān)鍵字的語句,在default語句中處理賦值和函數(shù)調(diào)用的分析。語句中的表達(dá)式通過expr函數(shù)處理,其處理的BNF如下:

            exp ::=  nil | false | true | Number | String | ‘...’ | functiondef |

                             prefixexp | tableconstructor | exp binop exp | unop exp

            expr函數(shù)調(diào)用subexpr函數(shù)完成處理。

             

            static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
              BinOpr op;
              UnOpr uop;
              enterlevel(ls);
              uop = getunopr(ls->t.token);
              if (uop != OPR_NOUNOPR) {
                int line = ls->linenumber;
                luaX_next(ls);
                subexpr(ls, v, UNARY_PRIORITY);
                luaK_prefix(ls->fs, uop, v, line);
              }
              else simpleexp(ls, v);
              /* expand while operators have priorities higher than `limit' */
              op = getbinopr(ls->t.token);
              while (op != OPR_NOBINOPR && priority[op].left > limit) {
                expdesc v2;
                BinOpr nextop;
                int line = ls->linenumber;
                luaX_next(ls);
                luaK_infix(ls->fs, op, v);
                /* read sub-expression with higher priority */
                nextop = subexpr(ls, &v2, priority[op].right);
                luaK_posfix(ls->fs, op, v, &v2, line);
                op = nextop;
              }
              leavelevel(ls);
              return op;  /* return first untreated operator */
            }

             

            當(dāng)分析exp binop exp | unop exp的時(shí)候lua采用的是算符優(yōu)先分析,其各個(gè)運(yùn)算符的優(yōu)先級(jí)定義如下:

             

            static const struct {
              lu_byte left;  /* left priority for each binary operator */
              lu_byte right; /* right priority */
            } priority[] = {  /* ORDER OPR */
               {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7},  /* `+' `-' `*' `/' `%' */
               {10, 9}, {5, 4},                 /* ^, .. (right associative) */
               {3, 3}, {3, 3}, {3, 3},          /* ==, <, <= */
               {3, 3}, {3, 3}, {3, 3},          /* ~=, >, >= */
               {2, 2}, {1, 1}                   /* and, or */
            };

            #define UNARY_PRIORITY     8  /* priority for unary operators */

             


            代碼生成


            lua代碼生成是伴隨著語法分析進(jìn)行的,指令類型Instruction定義在llimits.h中:

             

            /*
            ** type for virtual-machine instructions
            ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
            */
            typedef lu_int32 Instruction;

             


            Instruction是一個(gè)32位的整形數(shù)據(jù),其中0~5 bits表示optype,6~13 bits參數(shù)A,14~22 bits表示參數(shù)B,23~31 bits表示參數(shù)C,14~31 bits表示參數(shù)Bx或sBx,6~31 bits表示參數(shù)Ax。

            代碼生成的函數(shù)聲明在lcode.h中,以luaK開頭,這一系列的函數(shù)大多都有expdesc *v的參數(shù),expdesc的結(jié)構(gòu)定義在lparser.h,如下:

             

            typedef struct expdesc {
              expkind k;
              union {
                struct {  /* for indexed variables (VINDEXED) */
                  short idx;  /* index (R/K) */
                  lu_byte t;  /* table (register or upvalue) */
                  lu_byte vt;  /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */
                } ind;
                int info;  /* for generic use */
                lua_Number nval;  /* for VKNUM */
              } u;
              int t;  /* patch list of `exit when true' */
              int f;  /* patch list of `exit when false' */
            } expdesc;

             

            expdesc中的t和f分別表示表達(dá)式為true和false時(shí),待回填跳轉(zhuǎn)指令的下標(biāo)。k表示表達(dá)式的類型,u表示對(duì)應(yīng)類型的數(shù)據(jù)。

            代碼生成過程中根據(jù)表達(dá)式類型做相應(yīng)的代碼生成操作,lua中每個(gè)函數(shù)最大有250個(gè)寄存器,表達(dá)式的計(jì)算就是選擇這些寄存器存放并生成數(shù)據(jù),而寄存器的下標(biāo)是在代碼生成階段選擇好的,寄存器的釋放是根據(jù)變量和表達(dá)式的生命周期結(jié)束的時(shí)候釋放。代碼生成過程會(huì)將變量的生命周期的起始pc和結(jié)束指令pc分別存放在Proto中的LocVar的startpc和endpc里面,供調(diào)試使用。

            posted @ 2012-08-12 17:28 airtrack 閱讀(8600) | 評(píng)論 (0)編輯 收藏
            很早就想讀lua的源碼,也曾很多次瀏覽過大概。不過我一直沒有深入去讀,一是想自己在讀lua源碼之前,僅憑自己對(duì)lua使用的理解自己先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的lua子集,二是我覺得自己實(shí)現(xiàn)過lua的子集之后也能幫助自己更容易的理解lua源碼。前段時(shí)間,花了幾個(gè)月的業(yè)余時(shí)間,實(shí)現(xiàn)了一個(gè)簡(jiǎn)單粗糙的lua子集(https://github.com/airtrack/luna)之后,我覺得現(xiàn)在可以開始讀lua的源碼了。

            從lua.c的main函數(shù)開始,lua.c是一個(gè)stand-alone的解釋器,編譯完就是一個(gè)交互式命令行解釋器,輸入一段lua代碼,然后執(zhí)行并返回結(jié)果,也可以執(zhí)行一個(gè)lua文件。

            main:
              /* call 'pmain' in protected mode */
              lua_pushcfunction(L, &pmain);
              lua_pushinteger(L, argc);  /* 1st argument */
              lua_pushlightuserdata(L, argv); /* 2nd argument */
              status = lua_pcall(L, 2, 1, 0);
              result = lua_toboolean(L, -1);  /* get result */

            main函數(shù)創(chuàng)建了lua_State之后就按照調(diào)用C導(dǎo)出給lua函數(shù)的方式調(diào)用了pmain函數(shù)。pmain函數(shù)中通過lua棧獲取到命令行的argc和argv參數(shù)之后,對(duì)參數(shù)進(jìn)行分析后,主要可以分為兩個(gè)分支,一個(gè)處理交互命令行,一個(gè)處理文件。dotty出來交互命令行,handle_script處理lua文件。

            handle_script:
              status = luaL_loadfile(L, fname);
              lua_insert(L, -(narg+1));
              if (status == LUA_OK)
                status = docall(L, narg, LUA_MULTRET);
              else
                lua_pop(L, narg);

            在handle_script中先loadfile,然后docall。

            loadfile會(huì)產(chǎn)生一個(gè)什么東西在棧上呢?寫過lua的程序的人估計(jì)都會(huì)了解到下面這段lua代碼:
            local f = load(filename)
            f()
            load會(huì)將文件chunk編譯成一個(gè)function,然后我們就可以對(duì)它調(diào)用。如果我們?cè)敿?xì)看lua文檔的話,這個(gè)函數(shù)可以帶有upvalues,也就是這個(gè)函數(shù)其實(shí)是一個(gè)閉包(closure)。按照我自己實(shí)現(xiàn)的那個(gè)粗糙的lua子集的方式的話,每個(gè)運(yùn)行時(shí)期的可調(diào)用的lua函數(shù)都是閉包。
            #define luaL_loadfile(L,f)     luaL_loadfilex(L,f,NULL)

            luaL_loadfilex:
              if (filename == NULL) {
                lua_pushliteral(L, "=stdin");
                lf.f = stdin;
              }
              else {
                lua_pushfstring(L, "@%s", filename);
                lf.f = fopen(filename, "r");
                if (lf.f == NULL) return errfile(L, "open", fnameindex);
              }
              if (skipcomment(&lf, &c))  /* read initial portion */
                lf.buff[lf.n++] = '\n';  /* add line to correct line numbers */
              if (c == LUA_SIGNATURE[0] && filename) {  /* binary file? */
                lf.f = freopen(filename, "rb", lf.f);  /* reopen in binary mode */
                if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
                skipcomment(&lf, &c);  /* re-read initial portion */
              }
              if (c != EOF)
                lf.buff[lf.n++] = c;  /* 'c' is the first character of the stream */
              status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);

            luaL_loadfile是一個(gè)宏,實(shí)際是luaL_loadfilex函數(shù),在luaL_loadfilex函數(shù)中,我們發(fā)現(xiàn)是通過調(diào)用lua_load函數(shù)實(shí)現(xiàn),lua_load的函數(shù)原型是:
            LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode);
            定義在lapi.c中,它接受一個(gè)lua_Reader的函數(shù)并把data作為這個(gè)reader的參數(shù)。在luaL_loadfilex函數(shù)中傳給lua_load作為reader是一個(gè)static函數(shù)getF,getF通過fread讀取文件。

            lua_load:
              ZIO z;
              int status;
              lua_lock(L);
              if (!chunkname) chunkname = "?";
              luaZ_init(L, &z, reader, data);
              status = luaD_protectedparser(L, &z, chunkname, mode);

            在函數(shù)lua_load中,又將lua_Reader和data通過luaZ_init函數(shù)把數(shù)據(jù)綁定到ZIO的結(jié)構(gòu)中,ZIO是buffered streams。之后調(diào)用luaD_protectedparser,此函數(shù)定義在ldo.c中,在這個(gè)函數(shù)中,我們發(fā)現(xiàn)它使用了構(gòu)造lua_Reader和data的方式構(gòu)造了調(diào)用函數(shù)f_parser和它的數(shù)據(jù)SParser,并將它們傳給luaD_pcall,luaD_pcall的功能是在protected模式下用SParser數(shù)據(jù)調(diào)用f_parser函數(shù),因此我們只需追蹤f_parser函數(shù)即可。

            luaD_protectedparser:
            status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);

            f_parser:
              if (c == LUA_SIGNATURE[0]) {
                checkmode(L, p->mode, "binary");
                cl = luaU_undump(L, p->z, &p->buff, p->name);
              }
              else {
                checkmode(L, p->mode, "text");
                cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
              } 

            f_parser通過數(shù)據(jù)頭的signature來判斷讀取的數(shù)據(jù)是binary還是text的,如果是binary的數(shù)據(jù),則調(diào)用luaU_undump來讀取預(yù)編譯好的lua chunks,如果是text數(shù)據(jù),則調(diào)用luaY_parser來parse lua代碼。我們發(fā)現(xiàn)luaU_undump和luaY_parser函數(shù)的返回值都是Closure *類型,這個(gè)剛好就和我們前面預(yù)計(jì)的一樣,一個(gè)chunk load之后返回一個(gè)閉包。

            進(jìn)入luaY_parser函數(shù)后,就調(diào)用了一個(gè)static的mainfunc開始parse lua代碼。

            仔細(xì)回顧上面看過的函數(shù),我們會(huì)發(fā)現(xiàn)每個(gè)C文件的導(dǎo)出函數(shù)都會(huì)使用lua開頭,如果沒有l(wèi)ua開頭的函數(shù)都是static函數(shù)。并且我們會(huì)發(fā)現(xiàn)lua后的大寫前綴可以標(biāo)識(shí)這個(gè)函數(shù)所屬的文件:
            luaL_loadfile luaL_loadfilex L應(yīng)該是library的意思,屬于lauxlib
            luaD_protectedparser luaD_pcall D是do的意思,屬于ldo
            luaU_undump U 是undump的意思,屬于lundump
            luaY_parser Y 是代表yacc的意思,lua的parser最早是用過yacc生成的,后來改成手寫,名字也保留下來,屬于lparser
            其它的lua函數(shù)也都有這個(gè)規(guī)律
            posted @ 2012-07-19 18:24 airtrack 閱讀(12542) | 評(píng)論 (3)編輯 收藏

            vimer、emacser的優(yōu)越感

            曾幾何時(shí),剛學(xué)編程沒多久,網(wǎng)上看到一群“牛人”吹噓說世界上有三種編輯器:一種是vim,一種是emacs,一種是其它。
            當(dāng)時(shí)看到各種介紹vim和emacs的文章都是頂禮膜拜的,希望自己哪天也能成為那種能玩的動(dòng)“神器”。一直是水平不夠或者其它原因,沒學(xué)會(huì)。3年多前看到一個(gè)vim的視頻,當(dāng)時(shí)下狠心終于把vim學(xué)會(huì)了,當(dāng)然有之前一兩年斷斷續(xù)續(xù)學(xué)vim的基礎(chǔ)的幫助。自從學(xué)會(huì)了vim之后我也加入到了vimer行列,終于學(xué)會(huì)了“神器”編輯器。終于可以在別人討論其它編輯器的時(shí)候回復(fù)一句裝逼的“vimer飄過”的語句。前輩們是說vim、emacs高效,因?yàn)槟銓W(xué)會(huì)了之后,你的手不需要離開主鍵盤區(qū)域。其實(shí)在我看來這完全不是理由,其它編輯器的各種快捷鍵同樣能夠保證你不離開主鍵盤區(qū)域完成編輯功能,只不過普通編輯器不會(huì)強(qiáng)迫你學(xué)習(xí)快捷鍵,而vim和emacs是你必須學(xué)會(huì)快捷鍵才能夠使用。這時(shí)“牛人”也許會(huì)說vim和emacs都有超強(qiáng)的定制性,可以定制你想要的“任何”功能。看起來是很牛逼的,vim和emacs是有不少很強(qiáng)力的插件,可以把它們定制的很強(qiáng)力,但是要說到“任何”那也只是停留在理論上。C++自動(dòng)提示功能始終都是vim和emacs的痛,幸好有了clang,自動(dòng)提示能力終于上了一個(gè)檔次,但是那流暢程度和VA比起來還是要差一點(diǎn),畢竟是基于單個(gè)文件分析(每次改動(dòng)都會(huì)重新分析整個(gè)文件),不像VA那樣是整個(gè)工程分析。至于C++的重構(gòu)功能,到現(xiàn)在vim和emacs都沒有很好的實(shí)現(xiàn),不要說重構(gòu)在C++里面沒有用,至少我覺得rename和extract method還是很有用的。vim和emacs其實(shí)沒有牛人吹噓的那么神奇,當(dāng)然它們確實(shí)是很優(yōu)秀的編輯器。最近一段時(shí)間我在減少使用vim,是因?yàn)榻?jīng)常敲擊ctrl鍵導(dǎo)致左手小指有時(shí)會(huì)疼痛。這個(gè)問題也許會(huì)在emacser上面更加嚴(yán)重,emacser都是迫切需要“腳踏板”的。
            vimer和emacser的優(yōu)越感是從前輩“牛人”那里聽來,費(fèi)勁力氣學(xué)會(huì),終于可以對(duì)沒學(xué)會(huì)的人來上一句“你的編輯器是其它”產(chǎn)生的。

            蘋果系的優(yōu)越感

            蘋果的產(chǎn)品通常比一般的產(chǎn)品有著更貴的價(jià)格,通常用戶體驗(yàn)比起一般的產(chǎn)品也確實(shí)要好,這往往成為某些裝逼人事的裝逼利器。當(dāng)一部分人用上了“先進(jìn)”的蘋果產(chǎn)品,開始寫各種文章炫耀蘋果的優(yōu)越性,比起其它怎么怎么好,使得很多沒有試用過蘋果產(chǎn)品的人心生向往,費(fèi)盡力氣也要體驗(yàn)體驗(yàn)。當(dāng)這些人費(fèi)盡力氣使用了蘋果的產(chǎn)品后,有很大一部分人自然的覺得自己用上了高端的產(chǎn)品,往往產(chǎn)生優(yōu)越感。有不少使用Macbook Pro的人說使用Macbook Pro再也沒有關(guān)過機(jī),什么東西都是合上就走,并以此產(chǎn)生對(duì)Windows的優(yōu)越感,說Windows是不可能做到。然而我身邊就有一個(gè)同事使用thinkpad,裝的是Windows XP,而他的機(jī)器一年都是沒關(guān)過機(jī),都是合上就走的,這更別說Windows 7了。我自己從去年開始使用Macbook air,剛開始使用第一周死過一次機(jī),后來也出現(xiàn)過一次死機(jī)。我覺得Macbook air是不錯(cuò),但是還不至于說甩開其它產(chǎn)品幾條街,讓人產(chǎn)生強(qiáng)烈的優(yōu)越感。
            使用蘋果系的人產(chǎn)生優(yōu)越感往往是因?yàn)樽约焊冻隽吮容^大的一部分資金后,看到產(chǎn)品的不少優(yōu)點(diǎn)之后就開始無視對(duì)于其它產(chǎn)品的缺點(diǎn),從而產(chǎn)生一種高貴的優(yōu)越感。

            Linux程序員的優(yōu)越感

            有不少Linux程序員,覺得自己是Linux程序員能干不少牛逼的事,能看到優(yōu)秀的源碼。就連調(diào)用系統(tǒng)調(diào)用都能產(chǎn)生優(yōu)越感,說Linux的系統(tǒng)調(diào)用簡(jiǎn)單明了,比起Windows的API來說簡(jiǎn)單。這當(dāng)然是個(gè)優(yōu)點(diǎn),但這就能讓人產(chǎn)生優(yōu)越感。而往往即懂Windows又懂Linux的人的卻能夠更好更正確的認(rèn)識(shí)各個(gè)系統(tǒng)的優(yōu)缺點(diǎn)。我了解到一些Linux程序員會(huì)產(chǎn)生優(yōu)越感,有不少是曾今學(xué)習(xí)Windows編程,發(fā)現(xiàn)自己沒能學(xué)好(往往是學(xué)習(xí)GUI編程沒學(xué)好),然后看到很多網(wǎng)上牛人都使用Linux,然后轉(zhuǎn)移到Linux潛心學(xué)習(xí),編寫命令行程序,終于修煉成功,之后就開始噴Windows多么不好,進(jìn)而產(chǎn)生優(yōu)越感。

            C程序員的優(yōu)越感

            C程序員的優(yōu)越感的產(chǎn)生有點(diǎn)類似Linux程序員,而往往C程序員也就是Linux程序員。有了Linux的優(yōu)越感之后,更加的認(rèn)為只要有Linux和C就能解決所有問題,只要比C更復(fù)雜的東西都是不值得的。而這些C程序員自然而然的把優(yōu)越感產(chǎn)生建立在C++之上,而且是這個(gè)也是有一定的相似性,也是帶著C的思維學(xué)習(xí)C++,發(fā)現(xiàn)不少C++的東西不是按照他想象的那樣運(yùn)作之后,就開始鄙視C++最終又回歸為C,而往往也產(chǎn)生對(duì)C++程序員的優(yōu)越感。不過再我看來,如果能夠成為一個(gè)優(yōu)秀的C++程序員,你讓他回去寫C代碼,他同樣能夠?qū)懗鰞?yōu)秀的C代碼來。C程序員的優(yōu)越感其實(shí)有些可悲,往往是自己短視,可以不喜歡不使用一種語言,但是這完全不是產(chǎn)生優(yōu)越感的理由。

            技術(shù)等級(jí)的優(yōu)越感

            一般公司都有技術(shù)等級(jí)之分,高級(jí)工程師一般工作經(jīng)驗(yàn)比普通工程師要豐富一點(diǎn),抑或是在某些方面比較擅長(zhǎng)。而他們對(duì)待普通工程師的時(shí)候往往產(chǎn)生一種“我什么都應(yīng)該比普通工程師懂的優(yōu)越感”,跟普通工程師討論問題的時(shí)候往往帶著一種高級(jí)工程師的優(yōu)越感,覺得普通工程師的各個(gè)方面都不如自己的感覺,因而形成一種嚴(yán)格的等級(jí)制度,時(shí)間長(zhǎng)了之后就變成了一種“文化”。這種優(yōu)越感似乎是有傳遞性的,等那些普通工程師終于熬成高級(jí)之后也開始對(duì)后來的普通工程師產(chǎn)生優(yōu)越感。
            還有其它不少情況很多人會(huì)對(duì)某些人某些東西產(chǎn)生優(yōu)越感,這種優(yōu)越感的產(chǎn)生一般都是因?yàn)楦冻隽烁嗟哪硺訓(xùn)|西之后,自然的對(duì)事物的分級(jí)而產(chǎn)生,覺得自己的層級(jí)更高一點(diǎn),自然而然的產(chǎn)生了優(yōu)越感。當(dāng)這種優(yōu)越感開始在一定范圍內(nèi)開始傳播之后,對(duì)于某些曾今不能體會(huì)到優(yōu)越感的人同樣付出了更多的某樣?xùn)|西之后,像病毒式的也感染了這種優(yōu)越感。使得這種優(yōu)越感一直往下傳遞。
            最近發(fā)現(xiàn)身邊和網(wǎng)上不少這種優(yōu)越感案例,有感而發(fā),寥寥幾筆。
            posted @ 2012-05-15 19:17 airtrack 閱讀(4213) | 評(píng)論 (21)編輯 收藏
                 摘要: BitWave的Host:
            源碼放在github上,采用NEW BSD LICENSE發(fā)布。地址:https://github.com/airtrack/bitwave  閱讀全文
            posted @ 2011-05-29 17:39 airtrack 閱讀(4681) | 評(píng)論 (8)編輯 收藏

            用Lua也有大半年了,從用Lua開始就想寫個(gè)Lua調(diào)試器,不過由于種種原因沒寫,這周上班抽了點(diǎn)時(shí)間寫了(我承認(rèn)上班偷懶了,不過多是休息時(shí)間)。(點(diǎn)此下載

            Lua本身沒有提供調(diào)試器,不過它自帶了一個(gè)debug庫,提供了基本的變量值獲取和代碼執(zhí)行hook,有了這些基本功能要寫一個(gè)調(diào)試器不難。

            此調(diào)試器根據(jù)調(diào)試方式分為normal、step in、step over、next line四種mode,分別對(duì)應(yīng)斷點(diǎn)、步進(jìn)、跳出函數(shù)、執(zhí)行下行的功能。斷點(diǎn)類型分為行斷點(diǎn)和函數(shù)斷點(diǎn),分別在執(zhí)行到相應(yīng)行和相應(yīng)函數(shù)的時(shí)候斷下。在斷下的時(shí)候就可以打印和修改變量,通過建立一個(gè)新的chunk并將環(huán)境設(shè)置成相應(yīng)函數(shù)的環(huán)境,再執(zhí)行chunk來獲取和修改變量。

            因?yàn)槭敲钚械模诿钚羞€沒有機(jī)會(huì)添加斷點(diǎn)的時(shí)候,要添加斷點(diǎn)就要通過debugger.addfuncbreak和debugger.addlinebreak來添加函數(shù)和行斷點(diǎn),通常Lua是用于C++的腳本語言,因此程序通常是有一個(gè)可以直接執(zhí)行Lua腳本指令的入口,這樣的入口就可以打下第一個(gè)斷點(diǎn),這樣在斷下斷點(diǎn)后就可以在命令提示符下做所有的操作了。

            代碼是上班抽時(shí)間寫的,寫的很隨意,也沒多少注釋,權(quán)當(dāng)玩具吧。目前的功能基本能滿足我的要求了,也不打算繼續(xù)改進(jìn)了。調(diào)試器是用來幫助找錯(cuò)誤,不要過分依賴調(diào)試器。Robert C. Martin說Debuggers are a wasteful Timesink。雖說有些偏激,但是不無道理。

            posted @ 2011-01-01 01:44 airtrack 閱讀(4792) | 評(píng)論 (3)編輯 收藏
            僅列出標(biāo)題
            共2頁: 1 2 
            777米奇久久最新地址| 国产精品日韩深夜福利久久| 久久久久久精品免费免费自慰| 欧美成人免费观看久久| 99久久精品国产高清一区二区 | 99久久久精品免费观看国产| 国产精品综合久久第一页| 国产精品女同一区二区久久| 久久精品免费一区二区| 久久99毛片免费观看不卡| 区久久AAA片69亚洲| 色综合久久精品中文字幕首页| 国产美女亚洲精品久久久综合| 伊人色综合久久天天| AV无码久久久久不卡网站下载| 66精品综合久久久久久久| 久久免费看黄a级毛片| 久久国产成人午夜aⅴ影院| 久久精品国产亚洲麻豆| 久久无码AV中文出轨人妻| 99久久99久久精品国产片果冻| 免费久久人人爽人人爽av| 国产精品女同一区二区久久| 国产精品一区二区久久| 亚洲va久久久噜噜噜久久| 99久久国产综合精品女同图片| 国产精品永久久久久久久久久| 久久狠狠高潮亚洲精品| 成人久久精品一区二区三区| 久久精品卫校国产小美女| 久久精品综合网| 久久综合久久伊人| 伊人久久综合无码成人网| 2021国内精品久久久久久影院| 国产成人久久久精品二区三区 | 大香伊人久久精品一区二区| 狠狠色伊人久久精品综合网| 亚洲一区中文字幕久久| 国产精品久久久99| 女同久久| 久久九九兔免费精品6|