词法分析实例

在《递归下降算法》一节里,介绍过数学表达式的词法分析,现在正式开始lua语言的词法分析。

以一段简单的lua代码为例:

-- 输出最大值
local a, b = 10.5, 20.00
local c = math.max(a, b)
local d = string.format(
  "maxval(%f,%f)=%f", a, b, c)
print(d)

代码逻辑为取a和b的最大值,赋给c,再打印a、b、c的值。

 

词法分析结果:

local a , b = 10.50 , 20.00 
local c = math . max ( a , b 
) local d = string . format 
( maxval(%f,%f)=%f , a , 
b , c ) print ( d ) <eof>

 

可以看出,lua的词法分析是按照以下几种类型区分的:

  • 标识符:如 local、a、b、math
  • 标点符号:如 , = ( )
  • 数字:如10.5、20.00
  • 字符串:如 "maxval(%f,%f)=%f"
  • 注释:以--开头的为注释

 

 

词法分析逻辑

lua的词法分析模块将源码文件作为一个字符流读入,每次取出一个字符,根据字符值做不同的处理,用代码实现就是一个超大switch语句。下面逐一介绍各个字符的处理逻辑。

 

\r 或 \n

linux系统里换行符为"\n",Windows系统换行符为"\r\n",为保证lua代码在两种系统行数一致,将 "\r\n" 和 "\n\r" 作为一行。

 

减号 -

在lua里'-'有多种可能的意义,如负号、减号、注释、多行注释。

  • 若下个字符不为'-',表示负号或减号,如 -100, a-b
  • 若下个字符为'-',且下下个字符为'[',按照多行注释处理,如 --[[ 多行注释 ]]
  • 否则按照单行注释处理,如 -- 单行注释

 

左方括号 [

可能为多行字符串,也可能为table取key操作。

  • 若为"[[" 或 "[=[" 等格式,读取多行字符串,如 [[ string ]]
  • 否则,表示取key操作,如 a["data"]=1

 

等号 =

  • 若下个字符为'=',表示相等比较,如 a==b
  • 否则,表示赋值操作,如 a=1

 

< 或 > 或 ~

  • 若下个字符为'=',表示小于等于、大于等于、不等于,如 <= >= ~=
  • 否则表示小于、大于

 

" 或 '

  • 表示字符串开始,如 "abc" 或 'abcd'

 

点号 .

  • 若为"...",表示变长参数,如 print(fmt, ...)
  • 若为"..",表示字符串连接符,如 a .. b
  • 若为".123",表示浮点数 0.123
  • 若为"a.b",表示table成员引用,等价于a["b"]

 

-1

  • 文件结束符EOZ

 

空白字符

  • 如 ' ' '\t' 

 

数值

  • 如 123、2.2、32e+1、0.2e-2 

 

标识符

  • 以字母或下划线开头,后面为字母、数字、下划线,如 abc123, _data

 

单操作符

  • 如 + - * / % 

 

 

数据结构

typedef union {
  lua_Number r;
  TString *ts;
} SemInfo;

typedef struct Token {
  int token;
  SemInfo seminfo;
} Token;

Token结构体用来表示一个分词,token表示分词类型,seminfo存储分词值

分词类型

token枚举

数值存储

举例

保留字

TK_FOR

 

for

保留字

TK_ELSE

 

else

数值

TK_NUMBER

seminfo.r

123

标识符

TK_NAME

seminfo.ts

myname

字符串

TK_STRING

seminfo.ts

"abc"

 

不同于常规的先做词法分析,再做语法分析的步骤,lua以语法分析为主,在需要的时候取出一个分词继续分析,所以取词函数luaX_next在许多地方被调用。

 

lua词法分析本身比较简单,核心逻辑在 llex.c 文件,代码量不足500行,感兴趣的可以看看。