Skip to content

Latest commit

 

History

History
171 lines (139 loc) · 6.98 KB

001_lua.md

File metadata and controls

171 lines (139 loc) · 6.98 KB

Lua5.1代码阅读(一):lua.c

一、Lua5.1相关资源

下载见:
http://luabinaries.sourceforge.net/download.html
在线版:
http://www.lua.org/source/5.1/
关于它的代码有一个阅读顺序,可以参考这篇文章:
http://www.reddit.com/comments/63hth/ask_reddit_which_oss_codebases_out_there_are_so/c02pxbp
Lua语言的Wiki条目
http://en.wikipedia.org/wiki/Lua_(programming_language)

二、lua.c概述

lua.c实现Lua独立解析器。
它在官网中被分类为Interpreter。
http://www.lua.org/source/5.1/lua.c.html
总行数391行。

三、lua.c头文件

#include <signal.h> #include <stdio.h>
#include <stdlib.h> #include <string.h>
#include "lua.h" #include "lauxlib.h"
` #include "lualib.h"

四、lua.c宏

  • #define lua_c
    lua_c是lua.c的缩写,用于打开luaconf.c第222-293行的独立程序宏配置。
    由于VC的宏展开问题,luaconf.c的那些宏可能无法直接用右键查找。

  • #define notail(x) {if ((x)[2] != '\0') return -1;}
    在静态方法collectargs中检查控制台命令行的单个字符串参数在结尾没有多余字符。
    用于诊断x的字符串长度为1,否则直接退出命令行解析。

五、静态全局变量

  • static lua_State *globalL = NULL;
    独立解析器的全局Lua状态机。
    它允许laction方法可以直接读取状态数据。
    它被laction读取,被pmain修改。
    全局的Lua虚拟机指针
    由于是在signal指定的句柄函数laction中使用,所以需要这样处理。

  • static const char *progname = LUA_PROGNAME;
    当前的程序名(短名,默认为lua),注意它的指针值可以被改变(但字符串是常量)。
    它被dotty和pmain修改,被print_usage、report和dotty读取。

六、静态全局方法

  • static void lstop (lua_State *L, lua_Debug *ar) {
    在出现signal中断时进行出错处理的lua钩子。
    只是简单地输出字符串"interrupted!"

  • static void laction (int i) {
    signal中断的处理句柄。
    它在docall中被注册。
    确保如果在lstop之前出现其他SIGINT,就直接结束进程(默认动作)
    然后用lua_sethook挂钩子lstop。

  • static void print_usage (void) {
    在stderr控制台输出中输出lua解析器的用法。

  • static void l_message (const char *pname, const char *msg) {
    公共的信息输出函数。

  • static int report (lua_State *L, int status) {
    运行完pmain后取出栈顶的出错信息然后用l_message输出。

  • static int traceback (lua_State *L) {
    出错处理的C函数,间接调用Lua标准库里的debug.traceback函数
    首先判断栈顶是不是出错信息的字符串message。
    然后调用debug.traceback(message, 2)输出当前Lua状态机的堆栈回溯。

  • static int docall (lua_State *L, int narg, int clear) {
    Lua保护模式运行VM(已经载入脚本后)
    先确保在出错时Lua状态机调用traceback这个C函数。
    然后用lua_pcall启动虚拟机。

  • static void print_version (void) {
    用l_message输出版本信息

  • static int getargs (lua_State *L, char **argv, int n) {
    把argv字符串数组压入L栈中,变成一个Lua表。

  • static int dofile (lua_State *L, const char *name) {
    执行Lua脚本文件。
    首先用luaL_loadfile加载(编译)文件,然后用docall执行。
    最后用report报告返回的状态值。

  • static int dostring (lua_State *L, const char *s, const char *name) {
    执行Lua脚本字符串。
    类似dofile,不过用luaL_loadbuffer代替luaL_loadfile。

  • static int dolibrary (lua_State *L, const char *name) {
    用Lua标准库的require函数加载脚本。

  • static const char *get_prompt (lua_State *L, int firstline) {
    从Lua全局变量_PROMPT和_PROMPT2获取首行或次行的命令行提示符,
    如果没有定义,默认是
    #define LUA_PROMPT "> " #define LUA_PROMPT2 ">> "
    它们定义在luaconf.h中的#if defined(lua_c) || defined(luaall_c)内。

  • static int incomplete (lua_State *L, int status) {
    在状态机出现语法出错后判断是否遇到<eof>
    用于交互模式输入时的多行预测(有可能当前还没有输入完)。

  • static int pushline (lua_State *L, int firstline) {
    交互模式,读取stdin的一行,无循环。

  • static int loadline (lua_State *L) {
    交互模式,读入stdin的一行,然后循环读取余下未输入完的内容。

  • static void dotty (lua_State *L) {
    进入交互模式。

  • static int handle_script (lua_State *L, char **argv, int n) {
    注入Lua全局变量arg,然后执行脚本或执行stdin的输入内容。

  • static int collectargs (char **argv, int *pi, int *pv, int *pe) {
    第一次命令行扫描,获取大部分选项。

  • static int runargs (lua_State *L, char **argv, int n) {
    第二次命令行扫描,用Lua虚拟机执行-e(字符串)或-l(文件)的内容

  • static int handle_luainit (lua_State *L) {
    读取环境变量LUA_INIT指定的脚本(@开头)或命令

  • static int pmain (lua_State *L) {
    把主入口实现为Lua的C函数,在Lua虚拟机上注册运行。
    它依次执行以下操作,
    并且把返回值保存到其userdata参数(类型为Smain)的status域中:

    • 取出第一个参数(即lua_cpcall的第三参数)。
    • 把L保存到全局变量globalL。
    • 设置progname。
    • 关闭GC。
    • 打开所有标准库(可能使用标准库里的debug和require)。
    • 执行GC。
    • 执行初始化操作和命令行选项提取:handle_luainit、collectargs、print_version、runargs。
    • 根据获取的选项选择执行handle_script(执行脚本)、dotty(交互模式)、执行stdin内容。

七、全局结构体

  • struct Smain
    struct Smain { int argc;
    char **argv; int status;
    `};
    在main和pmain中使用。
    它是pmain的C函数参数。
    它在pmain中作为userdata被读出。
    在main中,它是一个结构体局部变量的类型,其地址传给lua_cpcall的第三参数。
    argc域和argv域是命令行参数(避免全局读)。
    status域是最近一次lua库函数的返回值,非0表示有错误。

八、公共全局方法

  • int main (int argc, char **argv) {
    解析器入口。argc是参数个数,argv是字符串数组,长度为argc。
    作为C函数的入口被控制台程序执行。
    wmain.c把__argc和__argv传递给这个函数,模拟控制台参数。
    它的作用是:
    • 创建Lua状态,程序唯一的数据交互中心。
    • 创建Smain结构体对象,保存argc和argv。
    • 用lua_cpcall执行pmain这个C函数,然后报告结果和关闭状态机。
    • 根据lua_cpcall的返回值和Smain结构体对象的状态值退出程序

九、值得观察的关键代码

  • docall
    观察Lua脚本文件或字符串执行前后的状态变化。
  • pmain
    观察两次命令行扫描后的变化。