lua词法分析原理介绍

来源:互联网 发布:手机必备软件下载 编辑:程序博客网 时间:2024/06/04 19:31

lua词法分析原理介绍

目录
一、内部实现
(a)lua保留字
(b)LexState结构介绍
(c)Token词法但愿
(d)llex:词法分析
(e)关于注释的一些想法
二、可优化方向
(a)continue和其他关键字实现
(b)lua底层编译加密
(c)细节优化
(d)调整保留字顺序加密


一、内部实现

(a)lua保留字

在这里存储了全部的lua保留字,存储在luaX_token里,这里存储的是tokens

static const char *const luaX_tokens [] = {
    "and", "break", "do", "else", "elseif",
    "end", "false", "for", "function", "goto", "if",
    "in", "local", "nil", "not", "or", "repeat",
    "return", "then", "true", "until", "while",
    "..", "...", "==", ">=", "<=", "~=", "::", "<eof>",
    "<number>", "<name>", "<string>"
};


(b)LexState结构介绍

typedef struct LexState {  int current;     当前读入的字符(char/int)  int linenumber;  输入的行的计数器  int lastline;    上一个行  Token t;     当前对比token  Token lookahead;  lookahead Token  struct FuncState *fs;  FuncState 解析器私有值  struct lua_State *L;lua_State虚拟机全局状态机,这里不做深入介绍了  ZIO *z;  这里可以通过lzio.h看出,这里是字符流,通过这里读取输入的字符流  Mbuffer *buff;  tokens的流存储器  struct Dyndata *dyd;  5.2新增,还未了解  TString *source;  这里存储的是当前资源的名字  TString *envn;  5.2新增,目前还不太了解  char decpoint;  定界符的值} LexState;

***TString

结构比较复杂,这里可以理解为一个lua自己定制的string类型,较为简便

***Mbuffer

包含{char* buffer,size_t n,size_t buffsize}

buffer应当是字符流的指针

n是字符流的长度(这里的值还是因为底下的luaZ_bufflen定义

buffsize表示字符流的大小

***FuncState状态机需要为输入的function生成编码,下面会专门做讲解

***Token 源于编译原理 Token词法单元,具体作用会在下文讲解

***lookahead源于编译原理 lookahead 输入中当前被扫描的终结符号通常被成为“向前看”符号,语法分析时会用到

(c)Token词法单元

这个词法单元包含2个参数

int token

SemInfo seminfo


SemInfo是一个union,可能是一个lua_Number,也可能是一个TString 用来存储词义信息

这是编译过程中的最小单元

(d)llex:词法分析

在llex.c文件中,llex方法是获取token的方法,也就是词法分析方法,通过这个方法得到token并且进行存储处理,再进行语法分析

static int llex(LexState *ls,SemInfo *seminfo)

这里会把ls作为状态存储器,然后江源代码加载进来进行分析

其中会有多条switch判断,根据各个case情况进行处理判别,返回token

例如在default case中就做了TK_NAME和相关保留字Token分析处理


Token会在luaX_init中初始化好相对应的TString tsv.extra 这里存储了需要用到的保留字信息

void luaX_init (lua_State *L) {  int i;  for (i=0; i<NUM_RESERVED; i++) {    TString *ts = luaS_new(L, luaX_tokens[i]);    luaS_fix(ts);  /* reserved words are never collected */    ts->tsv.extra = cast_byte(i+1);    }-------->就是在这里存储了保留字信息@!!!!!}

 然后在defualt中作如下操作:(这里是5.2代码)

static int llex (LexState *ls, SemInfo *seminfo) {  luaZ_resetbuffer(ls->buff);  for (;;) {    switch (ls->current) {      case '\n': case '\r': {  /* line breaks */        inclinenumber(ls);        break;      }      case ' ': case '\f': case '\t': case '\v': {  /* spaces */        next(ls);        break;      } …………      default: {        if (lislalpha(ls->current)) {  /* identifier or reserved word? */          TString *ts;//关于保留字的处理,存储到tsv.extra          do {            save_and_next(ls);          } while (lislalnum(ls->current));这里用来判别词法是否是数字和字母符号          ts = luaX_newstring(ls, luaZ_buffer(ls->buff),                                  luaZ_bufflen(ls->buff));          seminfo->ts = ts;          if (isreserved(ts))  /* reserved word? */            return ts->tsv.extra - 1 + FIRST_RESERVED;返回保留字标识符Token          else {返回name标识符Token            return TK_NAME;          }        }        else {  /* single-char tokens (+ - / ...) */          int c = ls->current;          next(ls);          return c;        }      }    }  }}

然后这些token就会存储在Mbuffer *buffer里面供给语法分析器进行语法分析


(e)关于注释的一些想法

在这里可以发现,lua关于注释的处理

我们会发现lua里面关于注释的过程,也会在程序运行时进行处理,如果将lua作为一个语言,那么应当将注释在预编译过程中直接移除

关于lua预编译部分应当有更进一步的研究


二、可优化方向

这里是将外部lua源码编译成lua虚拟机可识别指令的第一步,我们针对这里可以做的优化有很多

(a)continue和其他关键字实现

我们新建一个continue保留字,只需要在这里新增词法分析,那么continue就会成为一个lua中的保留字
然后再根据语法分析中,我们去设置continue语句在上下文中的语法表现,就可以实现continue
这里很明显就是在定义保留字的入口和实现基石

(b)lua底层编译加密

如果在这一层做加密,那么我们可以通过工具将源码进行加密替换,然后在这里实现对应的解密操作
这样解谜操作就相当于是现在了lua底层,用C语言实现了,同时保证lua源码不会暴露出来
这样的加密方式安全度会有所提高,但是实现成本会比较高,同时也要做到字节码的加密才能有效

(c)细节优化

如果lua的编译过程需要在运行时进行,那么这里的操作就会对效率产生一定的影响,最好将编译过程独立完成
可以尝试调整相关lua词法分析步骤,就可以对lua编译过程进行优化处理
最主要的优化应当在链接过程中处理,可能提高效率会比较高

(d)调整保留字顺序加密

如果要调整保留字顺序,需要同时处理以下部分
llex.h ------------------------>enum RESERVED {}
llex.c ------------------------>static const char *const luaX_tokens[]{}

这类加密方法就比较简单了,在网上已经有解密方法了
原创粉丝点击