lua source code analysis and notes finishing II parse and process function calls

Previous record parsing local variables, finishing today at a function funciton parsing process and calls a virtual machine;

The main objective: how to store functions, parameters, how to store, how to perform a function and pass parameters, return values ​​how transfer.

To a simple example, the following code command line lua

local myfun = function(a,b)
    return a + b
end 

In general virtual machine is blocked in place to read input When you enter the code above, then look inside the virtual machine function, what will become.

Introduce storage structure function in a virtual machine: FuncState here with fs said that the next pick a few important speaking, something similar concept with luaState (state machine ???)

typedef struct FuncState{
    Proto * f ;   //类似c++代码段+函数栈,真正存储字节码和栈变量的地方
    Table*  h ;   //哈希表,用于快速查找常量所在数组(f->k[])对应的下标index
    int pc;       //指向当前需要执行的字节码,类似于指令寄存器 ip,每执行一条+1
    int freereg;  //当前空闲的栈序号,从0开始,每入栈一个常量变量等,freereg+1,出栈-1
    short nlocalvars;  //指示在f->locvars[]数组中有多少个元素(这个数组存局部变量)
    ......其他的用到再说

}FuncState;

Proto * f also introduced here under:

typedef struct Proto{
    TValue *k ;  //保存常量用的数组(向量)
    int sizek;
    Instruction *code;  //字节码真正保存处,fs->pc指向要执行的指令处
    struct LocVar *locvars;   //LocVar仅保存变量名和生命周期起始点和终点,用数组下标对应栈位置
    int sizelocars; //仅表示locvars的分配的空间,变量个数在fs->nlocvars中保存
    int numparams; //参数个数 function(a,b)中的a,b,会优先入栈占领1,2两个位置
    。。。。。//其他留待后续研究
}Proto;

1, parsing preparation skip directly into the statement function, local corresponds TK_LOCAL (lexical units or grammar unit? Do not have to get to the bottom, who cares)

case TK_LOCAL:{
    luaX_next(ls); //解析local后面的一个元素,这里是myfun,对应TK_NAME
    if(testnext(ls,TK_FUNCTION)
        localfunc(ls)
    else
         localstat(ls);//局部变量,上一篇介绍过了
    
}

Here enters localstat (ls), introduced a locally variable, here skip, substantially as follows: with new_localvar, to generate a variable and freereg + 1, represents a position occupied by a stack, in fact, nothing inside, until resolved End (End execution? here is a simulation execution) "=" is the real assignment back.

static void localstat(LexState *ls)   //LexState今天研究重点不在这,大致看成输入源和解析缓存
{
    .......
    do
    {
        new_localvar(ls,str_checkname(ls),nvars++);
    }while(testnext(ls,",));  //local变量定义可一次定义多个,用“,”隔开,如local a,b,c = 1,2,3
    //注意完成上面过程后 fs -> f->locvars[0].name = "myfun", fs->nlocvars = 1 了
    //其实是TString类型的,这里直接用字符串"myfun"方便理解了
    if (testnext(ls,"="))  //赋值表达式
        nexps = explist1(ls,&e); //读取"="右边的数值列表,本次示例仅一个function(a,b)的定义
    else
    {
        ....
        //仅占位,不赋值,如 local a,b,c
    }

    adjuxt_assign(ls,nvars,nexps,&e); //占用栈空间,调整freereg的数值
    ....

}

2, followed by thorough explist1 (LS, & E) , to see how the analytic function is the solution

explist1(ls,e)
{
    .....
    expr(ls, e)
    {
        subexpr(ls, e)
        {
            ....
            simpleexp(ls, e);  //经过层层调用,做种进入这个函数
        }
    }


}

Into a series of track and found entered simpleexp (ls, e) function, general assignment, functions are entered in this analytic functions

static void simpleexp(LexState* ls, expdesc * v)
{
    switch(ls->t.token)  //每个token都是解析到的一个单独的元素,如TK_NAME, TK_LOCAL等
    {
        .....
        case TK_FUNCTION:
        {
            luaX_next(ls);  //读取后面的“(”,返回
            body(ls, v, 0 , ls->linenumber);  //终于进入我们想要去的地方了,前面都是一直在函数外面打转转
        }
       .......
    }

}

3, officially entered the analytic function body

static void body(LexState* ls, expdesc *e, int needSelf, int line)
{
    FuncState fs;  //圈起来,要考, 这就是我们一开始介绍的函数保存的形式了
    open_func(ls, &fs);  //给fs->f指针new一个Proto,各种变量赋初值如,fs->freereg = 0,
                        //fs->pc = 0; fs->nlocvars = 0; fa->h = newTable等,最后将
                        //fs->h压入栈,将fs->f压入栈(解析完会不会出栈我们拭目以待)
                        //其中fs->f->souce保存了源代码文件名,可用于调试使用
    //注意,这里ls->fs已经是刚才圈起来的FuncState,不是一开始用的那个了
    //ls->fs是链表结构,链表头已经换新了
    checknext(ls,'(');  //左括号则读入下一个token(这里是a),否则报错
    parlist(ls);  //解析参数列表,直到遇到右括号')';这里稍微展开一下
    ---------------------------------------------------------------
     parlist展开
         {
                if(token != ')')
                do
                {
                    cast TK_NAME:
                    {
                        new_locvar(ls, str_checkname(ls), nparams++)
                        //熟悉的味道,只不过ls-fs换成了新的FuncState了
                        //表示给ls->fs 创建变量,占用当前函数的freereg(每个函数单独的栈)
                        break;
                    }    
                }while(token !=')');
                //解析完 a,b两个参数后, fs->nlocvars = 2, freereg = 0(还未调整),
                //fs->f->locvars[] = { "a", "b"}, fs->h[] = { {'a',1},{'b',1}}
                //当然,fs->h中的a,b需要哈希成对应的key保存,这里仅用于好理解
                //且value仅赋值为bool(1)类型,以防垃圾回收,如果是常量,则需存对应fs->k[]下标
                //这个下标是常量组成字节码的一部分
                。。。。
                luaK_reserveregs(fs.fs->nactvar);  // //里面仅是简单的 freereg += 2 这样
                                                    //子,表示a,b占用了两个栈层
                           
                
         }
    ----------------------------------------------------------------
    
    checknext(ls,')'); //确保是又括号,然后读入下一个token,这里是“return”
    chunk(ls);  //进入函数内部,然后又进入刚开始的statment(ls),用于解析内部元素
        ----------------------------------------------------------------------
            chunk(ls)展开:
                。。。。。   
                while(!islast)
                {
                    islast = statement(ls); //每次解析一行? 
                }
        ----------------------------------------------------------------------
    
    

}

4, then we returned to the statement (ls), the next track

static int statment(LexState * ls)
{
    int line = ls->linenumber;  //当前所解析的行,在源码文件中的第几行
    switch(ls->t.token)   //读入的是return a+b中的return
    {
        。。。
        case TK_RETURN:
           retstat(ls);
           --------------------------------------------------------------------
             retstat展开:
            {
                fs = ls->fs
                luaX_next(ls); //读取下一个token, "a+b"中的 a
                ...
                    nret = explist1(ls,&e);//好像我们来过?详见前面的 第二步
            
            }
            --------------------------------------------------------------------    
        
            return 1;  //表示已经结束
        ......
    }

}

 

5, after the same with the second step of the process, we went to the simpleexp (ls) here; here it is a bit different

static void simpleexp(LexState* ls, expdesc * v)
{
    switch(ls->t.token)  //每个token都是解析到的一个单独的元素,如TK_NAME, TK_LOCAL等
    {
        .....
        default:
        {
            primaryexp(ls,v);//这次没有进入TK_FUNCITON,而是TK_NAME ,进入default了,
            return; //解析完 a+b直接返回
        }
       .......
    }

}

 

6, enter primaryexp:

static void primaryexp( LexState* ls, expdesc* v)
{
    fs = ls->fs;
    prefixexp(ls,v); //解析当前token,并读入下一个token,是“+”号
    ----------------------------------------------------
    展开{
            switch(token)
            {
                case TK_NAME: 
                    singlevar(ls,v);
                    -----------------------------------------
                     展开:
                    {
                        singevaraaux(fs, varname, v ,1);//varname就是刚读入的'a'
                        //这个函数会调用searchvar搜索a,搜到后将变量在fs->f->locvars[]中
                        //的下标保存在v结构中,并返回VLOCAL,表示这是局部变量,在第几个
                        //栈位置上,在这是0
                    }

                    -----------------------------------------
                return;

            }
            
            
        }
    -----------------------------------------------------
    //经过上面的函数后v已经是( k= VLOCAL, info= 0 )的状态了
    for(;;)
    {
        Switch(token){
            。。。
            进入default: return; 
    
        :
    }
    
}

 

7, from primaryexp (ls, v); returns, the return to the fourth explist1-> expr-> subexpr in

explist1(ls,e)
{
    .....
    expr(ls, e)
    {
        subexpr(ls, e)
        {
            ....
            simpleexp(ls, e);  

            //经过一系列追踪,刚从simpleexp中返回

            op = getbinopr(token); //这里是“+”号,返回 OPR_ADD
            while(op != OPR_NONE) 
            {
                。。。。
                luaX_next(ls); //读入下一个token, “b”
                luaK_infix(fs, op, v);  
                ------------------------------------------------------
                 luaK_infix展开:
                    。。。
                    case OPR_ADD: 
                        luaK_exp2RK(fs, e);  //终于要生成第一条字节码指令了
                        --------------------------------------------
                            篇幅不够,简单说来,就是生成了OP_ADD,a,b指令
                            然后将指令保存在fs->f->code[pc]中,pc++,同时保存该指
                            令对应的源文件行号 f->lineinfo[pc] = line
                            后面的大概就是生成一条OP_RETURN指令。。。
                            再开一篇介绍后面的步骤吧
                        --------------------------------------------
                ------------------------------------------------------

            }
        }
    }


}
发布了23 篇原创文章 · 获赞 0 · 访问量 1855

Guess you like

Origin blog.csdn.net/u014750316/article/details/104002524