zoukankan      html  css  js  c++  java
  • lua源码学习篇三:赋值表达式解析的流程

    上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式。lua语言支持多变量赋值。本文先从单变量赋值表达式讲起。

    a = 1
    b = 2
    c = a + b

    对于简单的两个数的求和过程,lua源码是如何解析的呢?

    首先,当词法分析获取到第一个token为‘a’的类型是TK_NAME(285),然后是chunk函数,statment函数,走到exprstate函数:

    static void exprstat (LexState *ls) { /* stat -> func | assignment */

      FuncState *fs = ls->fs; struct LHS_assign v;/*保存等号左边的变量名*/

      primaryexp(ls, &v.v);/*处理等号左边的变量名*/

      if (v.v.k == VCALL) /* stat -> func */

      SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */

      else { /* stat -> assignment */

      v.prev = NULL;

      assignment(ls, &v, 1); } }

    其中,LHS_assign是一个包含expdesc结构体的链表,拥有指向另一个变量的指针*prev。每个expdesc代表一个变量,该链表用于保存等式左边的所有变量。

    表达式分割的函数最终会从primaryexp进入到prefixexp函数里,由于当前的token值为TK_NAME=285,走到singlevar,即表示单变量的解析函数。

    static void singlevar (LexState *ls, expdesc *var) {
      TString *varname = str_checkname(ls);
      FuncState *fs = ls->fs;
      if (singlevaraux(fs, varname, var, 1) == VGLOBAL)
        var->u.s.info = luaK_stringK(fs, varname);  /* info points to global name *//*指向变量名在寄存器的索引值*/
    }

    luaK_stringK的最终返回值为变量名'a'在fs->f->k这个数组中的索引值,保存在var->u.s.info。这个值在生成字节码时会用到。

    然后是singlevaraux,第一次进入改函数,fs != NULL,进入else,在当前层次查找变量,找不到自动递归到上层,即fs->prev指向的上层fs,最后返回VGLOBAL。

    static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
      if (fs == NULL) {  /* no more levels? */
        init_exp(var, VGLOBAL, NO_REG);  /* default is global variable */
        return VGLOBAL;
      }
      else {
        int v = searchvar(fs, n);  /* look up at current level */
        if (v >= 0) {
          init_exp(var, VLOCAL, v);
          if (!base)
            markupval(fs, v);  /* local will be used as an upval */
          return VLOCAL;
        }
        else {  /* not found at current level; try upper one */
          if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)/**/
            return VGLOBAL;
          var->u.s.info = indexupvalue(fs, n, var);  /* else was LOCAL or UPVAL */
          var->k = VUPVAL;  /* upvalue in this level */
          return VUPVAL;
        }
      }
    }

    最后通过luaK_stringK函数调用addK函数对变量‘a’进行处理。 luaH_set()一开始调用luaH_get()在全局变量表中查找该value是否存在, 存在则直接返回值.不存在则调用newkey()完成添加动作。最终变量名'a'会放到f->k这个数组中,并且会返回对应的索引,然后讲索引保存到字节码中。

    static int addk (FuncState *fs, TValue *k, TValue *v) {
      lua_State *L = fs->L;
      TValue *idx = luaH_set(L, fs->h, k);
      Proto *f = fs->f;
      int oldsize = f->sizek;
      if (ttisnumber(idx)) {
        lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
        return cast_int(nvalue(idx));
      }
      else {  /* constant not found; create a new entry */
        setnvalue(idx, cast_num(fs->nk));
        luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
                        MAXARG_Bx, "constant table overflow");
        while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
        setobj(L, &f->k[fs->nk], v);
        luaC_barrier(L, f, v);
        return fs->nk++;
      }
    }

    这时候,回到exprstat函数,等号左边的变量名处理完了。然后处理等号右边的值,调用assignment函数赋值。如果下一个token是逗号,说明是多变量赋值。本例中是单变量。nexps = explist1(ls, &e);用于处理等号右边的值的表达式,将结果存入&e中,并返回右值的个数,然后判断是表达式的个数是否和右值的个数相等。

    static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
      expdesc e;
      check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,
                          "syntax error");
      if (testnext(ls, ',')) {  /* assignment -> `,' primaryexp assignment */
        struct LHS_assign nv;
        nv.prev = lh;
        primaryexp(ls, &nv.v);
        if (nv.v.k == VLOCAL)
          check_conflict(ls, lh, &nv.v);
        assignment(ls, &nv, nvars+1);
      }
      else {  /* assignment -> `=' explist1 */
        int nexps;
        checknext(ls, '=');
        nexps = explist1(ls, &e);/*解析等号右边的值*/
        if (nexps != nvars) {
          adjust_assign(ls, nvars, nexps, &e);
          if (nexps > nvars)
            ls->fs->freereg -= nexps - nvars;  /* remove extra values */
        }
        else {
          luaK_setoneret(ls->fs, &e);  /* close last expression */
          luaK_storevar(ls->fs, &lh->v, &e);/*生成指令*/
          return;  /* avoid default */
        }
      }
      init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */
      luaK_storevar(ls->fs, &lh->v, &e);
    }

    表达式分析函数是通过subexpr函数进行递归下降分析。这个知识点以后会专门来讲,现在由于只是简单赋值,不会涉及到运算符优先级的问题。本例中最终调用的是simpleexp函数,进入case TK_NUMBER:

    static void simpleexp (LexState *ls, expdesc *v) {
      /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
                      constructor | FUNCTION body | primaryexp */
      switch (ls->t.token) {
        case TK_NUMBER: {
          init_exp(v, VKNUM, 0);/*传入寄存器位置为0*/
          v->u.nval = ls->t.seminfo.r;/*将浮点数1.0赋值给v->u.navl*/
          break;
        }
        case …………………………
      }
      luaX_next(ls);
    }

    最后,luaK_storevar函数会将右值保存在寄存器,并生成相应的指令码

    void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
      switch (var->k) {
        case VLOCAL: {
          freeexp(fs, ex);
          exp2reg(fs, ex, var->u.s.info);
          return;
        }
        case VUPVAL: {
          int e = luaK_exp2anyreg(fs, ex);
          luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
          break;
        }
        case VGLOBAL: {/*本例中是全局变量*/
          int e = luaK_exp2anyreg(fs, ex);//返回寄存器索引
          luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);//生成指令
          break;
        }
        case VINDEXED: {
          int e = luaK_exp2RK(fs, ex);
          luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
          break;
        }
        default: {
          lua_assert(0);  /* invalid var kind to store */
          break;
        }
      }
      freeexp(fs, ex);
    }

    最后调用luaK_codeABx生成指令,关于指令问题,下回再叙。

    int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
      lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
      lua_assert(getCMode(o) == OpArgN);
      return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
    }
  • 相关阅读:
    关于js原型链继承的一些复习
    echarts 柱状图
    js的属性监听
    改变input[type=range]的样式 动态滑动
    占位符 css
    JS简单实现:根据奖品权重计算中奖概率实现抽奖的方法
    layui navTree 动态渲染菜单组件介绍
    配置单机Kafka
    树莓派安装pip3以及扩展包的方法
    Gunicorn+Nginx+Flask项目部署
  • 原文地址:https://www.cnblogs.com/nazhizq/p/6520072.html
Copyright © 2011-2022 走看看