lua语法分析原理介绍

来源:互联网 发布:电煤指标数据代码 编辑:程序博客网 时间:2024/05/24 22:41

lua语法分原理介绍

******目录

一、内部实现

(a)语法分析介绍

(b)简单产生式举例

(c)lua中实现

(d)statement举例

(e)编译成字节码

二、可优化方向

(a)continue实现

(b)预编译宏实现

(c)加密方法



一、内部实现

(a)语法分析介绍

**注---关于语法分析我们仅针对关键词进行概述,这里不做深度解释,如果有兴趣的话可以去研究编译原理

产生式 ---可以解释为语法分析对应表,扫描过得语句如果与产生式相同,就做分析

lookahead ---向前看字符,表示分析器当前被扫描的终结符号,用来与产生式中的符号做匹配

基本规则 -----lookahead和产生式依次做对比,然后按照对比成功的产生式进行编码操作(这里编码成lua的编码指令)

FIRST(a) -----表示可以产生a的所有字符串的第一个字符的集合


(b)简单产生式距离

stmt -> expr;

if (expr) stmt

| for (optexpr;optexpr;optexpr) stmt

| other


optexpr -> nil

| expr


其中stmt表示语句,expr表示表达式

从上述描述来看stmt语句可以有

四种表现形式,直接表达式,if语句,for语句,other语句(这里是因为不想做过多列举将other设置为终结符)

FIRST(stmt)即为{expr,if,for,other}--这里讲解我们把expr和other当做是终结符,否则集合应该为{if,for} and {FIRST(other)} and {FIRST(expr)}(预测分析法需要保证FIRST集合不相交)

lookahead存储着读入的字符,先与FIRST(stmt)中字符做匹配

以for (;expr;expr) other 为例

1)lookahead初始化为“for”,在FIRST(stmt)中匹配到for

2)则lookahead变为“(”,匹配stmt产生式中“(”

3)lookahead变为“;”,stmt检测到optexpr,optexpr检测到没有匹配,默认为nil

4)lookahead不变仍为“;”,stmt检测到“;”

5)继续匹配推进

…………

(c)lua中实现

所有实现在lparser.h和lparser.c中完成

luaY_parser --------------->函数是所有语法分析的始端其中包含了词法分析状态机LexState

产生式 --------------->分为多个分析函数,在其中处理相应的产生式与lookahead字符对比

对比函数 --------------->基本通过check_match,同时check_condition,checknext,check这些用来处理特殊予以情况使用

lookahead --------------->LexState中存储了Token lookahead,这是一个token类型,具体可以通过LexState去看 

基本规则 --------------->运用switch实现,划分成case分别解析处理

FIRST() --------------->这里基本是switch实现的,通过case来保证每个case执行相对应的产生式,进行更进一步的处理

例如根据chunk分解为block再分解为statement……


(d)statement举例

static void statement (LexState *ls) {  int line = ls->linenumber;  /* may be needed for error messages */  enterlevel(ls);  switch (ls->t.token) {    case ';': {  /* stat -> ';' (empty statement) */      luaX_next(ls);  /* skip ';' */      break;    }    case TK_IF: {  /* stat -> ifstat */      ifstat(ls, line);      break;    }    case TK_WHILE: {  /* stat -> whilestat */      whilestat(ls, line);      break;    }    case TK_DO: {  /* stat -> DO block END */      luaX_next(ls);  /* skip DO */      block(ls);      check_match(ls, TK_END, TK_DO, line);      break;    }    case TK_FOR: {  /* stat -> forstat */      forstat(ls, line);      break;    }    case TK_REPEAT: {  /* stat -> repeatstat */      repeatstat(ls, line);      break;    }    case TK_FUNCTION: {  /* stat -> funcstat */      funcstat(ls, line);      break;    }    case TK_LOCAL: {  /* stat -> localstat */      luaX_next(ls);  /* skip LOCAL */      if (testnext(ls, TK_FUNCTION))  /* local function? */        localfunc(ls);      else        localstat(ls);      break;    }    case TK_DBCOLON: {  /* stat -> label */      luaX_next(ls);  /* skip double colon */      labelstat(ls, str_checkname(ls), line);      break;    }    case TK_RETURN: {  /* stat -> retstat */      luaX_next(ls);  /* skip RETURN */      retstat(ls);      break;    }    case TK_BREAK:   /* stat -> breakstat */    case TK_GOTO: {  /* stat -> 'goto' NAME */      gotostat(ls, luaK_jump(ls->fs));      break;    }    default: {  /* stat -> func | assignment */      exprstat(ls);      break;    }  }

这里就是statement的相关解析方法,根据FIRST字符来处理

(e)编译成字节码

根据语法分析结束后通过lcode.h定义的方法编译成字节码

API统一命名为luaK_

lcode.c中实现了这些方法,可以去参考学习,基本就是将语义翻译成指令集序列

二、可优化方向

(a)continue实现

1)可以通过token增加continue token进行词法分析,这里可以参考lua词法分析部分,此时保留字里面就会出现TK_CONTINUE字样

2)接下来在statement里可以新增case TK_CONTINUE进行针对这一token的分析

3)当前lua没有continue的实现,这一空缺根据我的观察,应当是在funcState里面没有关于循环开始位置的记录,这就导致没办法实现continue,这应该是最主要原因

4)所以我们可以选择在funcState中加入一个参数用来记录起始位置,例如添加一个int spc,专门用来记录开始位置即可

5)这样就可以通过类似于5.2中gotostat()方法,**(实现break指令通过跳转到funcState的jpc(PC待跳转列表),此时只需跳转到spc即可


(b)预编译宏的实现

之前一直认为,lua没有预编译会导致我们编码中会有很多冗余的全局变量,例如全局变量表中的全部信息,其实都可以在便一部分完成预编译,这样就奸商了很多内存开销

因此,tianjiadefine保留字和define的相关产生式,就可以完成相应的实现(但是涉及到exp枚举新增,会比较麻烦)

当然这样实现也只会在一个当前文件里生效,如果需要全局操作,有两个方案

A方案:

将define定义成一个constant类型,即expkind中VK 那么就需要对VK进行扩展,此时链接那边也需要修改,这样的实现就会相对比较麻烦

B方案:

我们直接写脚本,去处理所有define A= 1 部分,这里在makeFile里就可以完成,可以先预处理一遍全部文件,将define处理并记录,然后在后续编译过程总进行替换即可


(c)加密方法

关于加密,语法和此法分析,可以做深层加密解析

例如我们把lua脚本先做预处理加密,那么在加密过后,我们获取到的加密文本在编译过程中,也会解密,所以并不能起到很大的加密作用

如果我们将解密方法处理在lua语法词法编辑器里,那么解密部分就会变成黑盒,不会被破解出来

但是在编译成字节码时,代码还是会暴露出来,所以字节码部分也应当同时加密这样才能有很好的加密效果,但是相对成本也就会更高