码迷,mamicode.com
首页 > 其他好文 > 详细

Lua4.0 词法分析

时间:2015-08-13 16:07:20      阅读:146      评论:0      收藏:0      [点我收藏+]

标签:

在说语法分析之前,先说一下词法分析。

因为语法分析时会调用词法分析。

词法分析相关的文件为 llex.h,llex.c。

先来看一下词法分析的头文件。

RESERVED 保留字枚举。


Token 结构体,词法分析就是把输入源分析成一个个的 token。

这个词比较常见,不再翻译成中文,因为我也不知道它的准确的中文叫什么。


LexState 结构体,词法分析状态机,记录当前词法分析的状态以及一些相关的环境。


下面的是几个词分析的方法,注意 lua 中的命名约定。

lua*_ 开头的都是 lua 内部使用的 API。其中 * 表示不同的模块,比如这里的 luaX_ 表示的就是词法分析相关的接口。

而以 lua_ 开头的 API 则是对外的,比如 lua.h 中的那些。


下面来看下 llex.c 源代码文件。


next(LS) 宏从输入缓冲区中取得下一个字符, zgec 在之前的 ZIO 缓冲区中有介绍。


token2string 数组和 RESERVED 枚举一一对应。

注意它的注释,如果你要调整 RESERVED 枚举中的顺序,需要相应的也修改这里数组中元素的顺序。

这就是 RESERVED 上的注释的含义。

这个字符串数组 "while" 之前的元素都是保留字。

void luaX_init (lua_State *L) {
  int i;
  for (i=0; i<NUM_RESERVED; i++) {
    TString *ts = luaS_new(L, token2string[i]);
    ts->marked = (unsigned char)(RESERVEDMARK+i);  /* reserved word */
  }
}

词法分析的初始化,驻留保留的字符串。

设置 marked 标记以避免垃圾回收。

TString 数据结构和 luaS_new 后面再进行分析。


luaX_checklimit 通过名字可以看出,判断是否超限。


luaX_syntaxerror 语法错误。


luaX_error 出错了。


luaX_token2str 根据 token 取得相应的保留字符串。


luaX_invalidchar 非法字符。


inclinenumber 到下一行,如果行数过多,则出错。

void luaX_setinput (lua_State *L, LexState *LS, ZIO *z, TString *source) {
  LS->L = L;
  LS->lookahead.token = TK_EOS;  /* no look-ahead token */
  LS->z = z;
  LS->fs = NULL;
  LS->linenumber = 1;
  LS->lastline = 1;
  LS->source = source;
  next(LS);  /* read first char */
  if (LS->current == ‘#‘) {
    do {  /* skip first line */
      next(LS);
    } while (LS->current != ‘\n‘ && LS->current != EOZ);
  }
}

设置输入,包括 Lua 状态机,输入缓冲区,输入源名字,及其它的一些初始化。

程序最后判断首个字符是否为 ‘#‘,如果是,则跳过首行。

这是为了 linux 环境中 lua 程序做为可执行脚本时,跳过 #!/usr/bin/lua 这一行。

int luaX_lex (LexState *LS, SemInfo *seminfo);

这个方法才是词法分析程序里的主角。

每次调用这个方法都会返回一个 token。由语法分析 parser 来调用。

程序的主体是一个死循环里的一个 switch case 分支,以当前的字符为分析判断条件。

下面简单的看下它的各分支:

case ‘ ‘: case ‘\t‘: case ‘\r‘:  /* `\r‘ to avoid problems with DOS */
  next(LS);
  continue;

// 跳过空格,制表符,回车符。

case ‘\n‘:
  inclinenumber(LS);
  continue;

// 跳过换行,增加行号。

case ‘$‘:
  luaX_error(LS, "unexpected `$‘ (pragmas are no longer supported)", ‘$‘);
  break;

// 非法字符,报错。

case ‘-‘:
  next(LS);
  if (LS->current != ‘-‘) return ‘-‘;
  do { next(LS); } while (LS->current != ‘\n‘ && LS->current != EOZ);
  continue;

// 中划线,如果后接其它的字符,则返回它自己,也就是减号。

// 否则,就是注释,跳过注释行。

case ‘[‘:
  next(LS);
  if (LS->current != ‘[‘) return ‘[‘;
  else {
    read_long_string(LS, seminfo);
    return TK_STRING;
  }

// 单中括号,如果后面不是另一个单中括号,返回它。

// 否则,返回一个长字符串。

case ‘=‘:
  next(LS);
  if (LS->current != ‘=‘) return ‘=‘;
  else { next(LS); return TK_EQ; }

// 等号,如果后面不是等号,返回。

// 否则,返回相等。

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; }

// 大于号,根据后面的字符决定它是大于还是大于等于。

case ‘~‘:
  next(LS);
  if (LS->current != ‘=‘) return ‘~‘;
  else { next(LS); return TK_NE; }

// 不等号,根据后面的字符决定它是非,或者是不等。

case ‘"‘:
case ‘\‘‘:
  read_string(LS, LS->current, seminfo);
  return TK_STRING;

// 单引号或者双引号,返回字符串。

case ‘.‘:
  next(LS);
  if (LS->current == ‘.‘) {
    next(LS);
    if (LS->current == ‘.‘) {
      next(LS);
      return TK_DOTS;   /* ... */
    }
    else return TK_CONCAT;   /* .. */
  }
  else if (!isdigit(LS->current)) return ‘.‘;
  else {
    read_number(LS, 1, seminfo);
    return TK_NUMBER;
  }

// 点,根据点的多少返回相应的操作,如果是单个点后面接数字,返回数字。

case ‘0‘: case ‘1‘: case ‘2‘: case ‘3‘: case ‘4‘:
case ‘5‘: case ‘6‘: case ‘7‘: case ‘8‘: case ‘9‘:
  read_number(LS, 0, seminfo);
  return TK_NUMBER;

// 数字

case EOZ:
  return TK_EOS;

// 结束

case ‘_‘: goto tname;

// 下划线,跳到 tname 标号。

default:
  if (!isalpha(LS->current)) {
    int c = LS->current;
    if (iscntrl(c))
      luaX_invalidchar(LS, c);
    next(LS);
    return c;
  }

// 如果不是字母,判断是否为控件字符,如果是,返回非法字符。

// 否则返回字。

  tname: {  /* identifier or reserved word */
    TString *ts = luaS_new(LS->L, readname(LS));
    if (ts->marked >= RESERVEDMARK)  /* reserved word? */
      return ts->marked-RESERVEDMARK+FIRST_RESERVED;
    seminfo->ts = ts;
    return TK_NAME;
  }

// 根据是否是保留字符串返回保留字符串,或者返回一个标志符。

static const char *readname (LexState *LS);

读取标志符。

static void read_number (LexState *LS, int comma, SemInfo *seminfo);

读取数字

static void read_long_string (LexState *LS, SemInfo *seminfo);

读取长字符串

static void read_string (LexState *LS, int del, SemInfo *seminfo);

读取字符串


后记

本来想写得很详细。

可是写着写着,觉得很没有必要。

或者,我应该尝试着换一种方式去写。

我得想想,接下来到底该如何?

----------------------------------------

到目前为止的问题:

> TString 数据结构和 luaS_new 

> 函数原型优化 luaU_optchunk

> 打印函数原型 luaU_printchunk

> dump 函数原型 luaU_dumpchunk

> 语法分析 luaY_parser

----------------------------------------


Lua4.0 词法分析

标签:

原文地址:http://my.oschina.net/xhan/blog/491818

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!