语法分析器会调用词法分析器。
在语法分析之前,简单的看一下词法分析。
内存管理和 ZIO 输入在词法分析中会用到,因为它们相对比较孤立,不影响主流程的阅读。
上一个版本也看过它们了,这里就不再重复了。
词法分析最重要的函数就是
int luaX_lex (LexState *LS, SemInfo *seminfo);
如果你用其它的词法分析工具生成器,生成的词法分析器也会有个类似的函数。
这个函数主要就是从源代码中读出一个 token 返回给语法分析器。
在语法分析的 next 和 lookhead 中会调用到它:
static void next (LexState *ls) {
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TK_EOS; /* and discharge it */
}
else
ls->t.token = luaX_lex(ls, &ls->t.seminfo); /* read next token */
}
static void lookahead (LexState *ls) {
lua_assert(ls->lookahead.token == TK_EOS);
ls->lookahead.token = luaX_lex(ls, &ls->lookahead.seminfo);
}
可以看到,返回一个 token 类型和一个表示表示 token 信息的结构体。
typedef union {
lua_Number r;
TString *ts;
} SemInfo; /* semantics information */
typedef struct Token {
int token;
SemInfo seminfo;
} Token;
luaX_lex 返回的就是 Token 结构体中的 token 类型字段。
如果类型字段不足以描述它自己的话,Token 中的 seminfo 字段就派上用场了。
比如:
对于大于小于这种比较运算,直接返回 token 就完事儿了。
case '<': {
next(LS);
if (LS->current != '=') return '<';
else { next(LS); return TK_LE; }
}
case '>': {
next(LS);
if (LS->current != '=') return '>';
else { next(LS); return TK_GE; }
}
而对于像是数值 TK_NUMBER,字符串型 TK_STRING 则需要将具体的 value 设置给 seminfo。
可以认为 Token 结构体表示的为 type-value 或者 key-value 对。