Lua1.1 Lua 的设计和实现 (二)

转载出处:http://my.oschina.net/xhan/blog/309615

(接上篇)

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

实现

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

扩展语言总是由应用程序以某种方式解释执行的。简单的扩展语言可以直接从源代码进行解释执行。另一方面,嵌入式语言通常是强大的编程语言,具有复杂的语法和语义。一个更有效的嵌入式语言实现技术是设计适合语言需求的虚拟机,编译扩展程序成虚拟机的字节码,然后通过解释执行字节码来模拟虚拟机(Betz 1988, 1991; Franks 1991)。我们选择这种混合架构来实现Lua;和直接执行源代码相比,它拥有如下优点:

因为词法和语法解析只进行一次,可能在实际嵌入之前使用外部解析器,识别简单的早期错误,获得更短的开发周期和更快的执行速度;

如果使用一个外部编译器时,可以只提供字节码形式的扩展程序,也就是预编译,从而可以使加载更快,环境更安全,运行时更小(不过,连接几个预编译的扩展程序可能是一项艰巨的任务)。
这种架构率先用于 Smalltalk(Goldberg–Robson 1983; Budd 1987)(字节码就是从它那里借来的术语),也成功用于基于P码(Clark–Koehler 1982)的 UCSD 的 Pascal 系统。在这些系统中,字节码虚拟机被用来减少复杂性并提高可移植性。这个方法也用于移植 BCPL 编译器(Richards–Whitby-Strevens 1980)。
扩展程序的编译器代码可以使用标准工具生成,如 lex 和 yacc (Levine–Mason–Brown 1992)。构造编译器的好工具的存在,并在七十年代末被广泛使用是当时小语言的萌发的主要原因,特别是在Unix环境里。我们实现 Lua 时使用 YACC 进行语法分析。最初,我们使用的 lex 写的词法分析器。通过对生产程序进行性能分析,我们发现,这个模块占用了差不多一半的加载和执行程序的时间。然后,我们直接用 C 重写了这个模块;新的词法分析器的速度超过旧的两倍多。
-------------------

Lua 的虚拟机

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

Lua 中使用的虚拟机是一个堆栈机。这意味着它不具有随机存取存储器:所有的临时值和局部变量保存在栈里。此外,它不具有通用寄存器,只有几个特殊的控制寄存器来控制堆栈和程序的执行。这些寄存器栈底,栈顶和程序计数器(base of stack, top of stack and program counter)。
虚拟机的程序是指令序列,称为字节码。程序的执行是通过解释字节码实现的,每一次指令操作都在栈顶进行。例如,语句

a = b + f(c)

被编译为:

PUSHGLOBAL "b"

PUSHGLOBAL "f"

PUSHMARK

PUSHGLOBAL "c"

CALLFUNC

ADJUST 2

ADD

STOREGLOBAL "a"

Lua的虚拟机有大约有 60 条指令;相应地,能够使用 8 位的字节码进行表示。许多指令(例如,ADD)不需要额外的参数;这些指令直接在栈上运行,并且编译后的代码只占用一个字节。其他指令(例如,PUSHGLOBAL 和 STOREGLOBAL)需要额外的参数,需要超过一个字节的占用。因为参数可以采用一个,两个或四个字节,这在某些体系架构上造成了字节对齐问题,不过可以通过填充空(NOP)指令来解决边界对齐的问题。
许多指令只是为了优化而存在。例如,有一种 PUSH 指令,需要一个数字作为参数并将其压栈,但也有单字节优化版本用于常用值的压栈,例如 0 和 1。因此,我们有 PUSHNIL,PUSH0,PUSH2,PUSH3。这样的优化同时减少了字节码的空间占用,和指令执行的时间占用。
回想一下,Lua 支持多重赋值和多个返回值。所以,有时候,值列表必须在运行时调整到给定长度:如果实际值多于所需,那么多余的值会被扔掉;如果需要的值多于实际的,根据需要在列表中进行 nil 扩展。调整通过 ADJUST 指令在栈上完成。
尽管多重赋值和多重返回是 Lua 中的一个强大的功能,便它们同进也是编译器和解释器复杂度的一个重要来源。因为函数没有类型声明,编译器不知道函数会返回多少值。因此,调整必须在运行时完成。同样,编译器不知道函数使用多少参数。因为这个数字在运行时可能会有所不同,在 PUSHMARK 和 CALLFUNC 指令之间参数列表中是相等的。
一种扩展 Lua 使用由主机提供的函数的方法是将每个这样的函数赋值给字节码做为指令(Betz 1988)。虽然这种策略将简化解释器,但它的缺点是只有少于 200 个的外部函数可以添加,因为 Lua 中只有 8 位字节码,并且 Lua 自己已经使用了其中的 60 个做为根本指令。所以我们选择了宿主显式注册外部函数并且 像对待原生的Lua函数一样处理这些外部函数。因此,单一 CALLFUNC 指令就足够了;解释器根据被调用的函数类型决定该做什么。
一个相当不同的策略由 Franks 提出(1991):宿主中的所有外部函数可以被嵌入语言调用;不需要进行显式注册。这是通过阅读和解释由链接器生成的符号表来完成。该解决方案对应用程序员来说是很方便的,但是是不可移植的,依赖于符号表文件的格式和所使用操作系统的重定位策略(Franks 使用了一个特定的 DOS 编译器)。
-------------------

内部数据结构

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

正如前面所提到的,Lua 的变量没有类型;值才有。因此,值由拥有两个字段的结构体(struct)实现:一个类型(type)和一个包含实际值的联合体(union)。这些结构出现在栈和符号表中,符号表(symbol table)持有所有的全局符号。
数值直接存储到联合体中。字符串保存在一个数组中;字符串(string)类型的值是指向该数组指针。函数类型的值是指向字节码数组的指针。类型 Cfunction 值是实际指向宿主程序中 C 函数的指针,用户数据类型(userdata) 的值与之类似。
表(table)被实现为哈希表,由单独的链接处理哈希碰撞(这也就是为什么一个表中索引是任意的原因)。如果在创建表时给出了它的尺寸(size),那么该尺寸就被当作哈希表的大小来用。因此,通过给哈希表提供一个近似等于表中元素数目的尺寸,会减少一些哈希碰撞,从而得到更高效的索引位置。此外,如果表作为数组来用,也就是只有数值下标,在创建表时选择合适的尺寸可以做主保证没有哈希碰撞。
所有的 Lua 内部数据结构都是动态分配的数组。当这些数组中没有更多的空位置(free slots)时,会自动执行垃圾回收,Lua 的垃圾回复算法用的是标记-清除(mark-and-sweep)算法。如果没有空间被回收(由于所有的值都被引用中),则数组会重新分配,尺寸扩大一倍。
垃圾回收为程序员提供了便利,因为它避免了显式的内存管理。当 Lua 作为一个独立的语言(它经常是)来使用时,垃圾回收是很有价值的。然而,当 Lua 嵌入到宿主程序(这是它的主要目的)中使用时,垃圾回收给与 Lua 进行交互的应用程序员带来了新的烦恼:应注意不要把 Lua 中的表和字符串存储到 C 语言变量中,因为这些值可能在垃圾回收过程中被回收,如果在 Lua 中他们没有其它引用的话。更确切地说,程序员必须在控制返回到 Lua 之前明确拷贝这些值到 C 变量中。虽然这是一个不同的模式,但是它至少不比使用标准 C 语言库中的 malloc-free 协议的内存管理差。
--------------------------------------

结论

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

Lua 自 93 年中被广泛应用于生产中,执行以下任务:

应用程序中用户的配置;

通用数据录入,使用用户定义的确认程序;

用户界面的描述;

应用程序对象的编程说明;

存储结构化的图形图元文件,用于图形编辑器和应用程序之间的通信。
此外,Lua 是目前正在考虑的可一个视化编程系统的基础。
对用户和开发人员来说,在运行时加载并执行 Lua 程序使配置变得很简单。此外,通用的嵌入式语言的存在降低了语言的不兼容,并鼓励更好的设计,把应用程序的配置问题和应用程序其它的主要问题清楚的分割开来。
本文中所描述的 Lua 实现可以从匿名的 ftp 中下载:http://www.lua.org/ftp/lua-1.1.tar.gz
-------------------

致谢

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

感谢在 ICAD 和 TeCGraf 工作的全体员工使用和测试 Lua。文中提到正在开发中的工业应用和 PETROBRAS (CENPES) 和 ELETROBRAS (CEPEL) 的研究中心是合作伙伴关系。
--------------------------------------

参考文献

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

M. Abrash, D. Illowsky, "Roll your own minilanguages with mini-interpreters", Dr. Dobb‘s Journal 14 (9) (Sep 1989) 52–72.
A. V. Aho, B. W. Kerninghan, P. J. Weinberger, The AWK programming language, Addison-Wesley, 1988.
B. Beckman, "A Scheme for little languages in interactive graphics", Software, Practice & Experience 21 (1991) 187–207.
J. Bentley, "Programming pearls: little languages", Communications of the ACM 29 (1986) 711–721.
J. Bentley, More programming pearls, Addison-Wesley, 1988.
D. Betz, "Embedded languages", Byte 13 #12 (Nov 1988) 409–416.
D. Betz, "Your own tiny object-oriented language", Dr. Dobb‘s Journal 16 (9) (Sep 1991) 26–33.
T. Budd, A Little Smalltalk, Addison-Wesley, 1987.
R. Clark, S. Koehler, The UCSD Pascal handbook: a reference and guidebook for programmers, Prentice-Hall, 1982.
M. Cowlishaw, The REXX programming language, Prentice-Hall, 1990.
L. H. de Figueiredo, C. S. de Souza, M. Gattass, L. C. G. Coelho, "Geração de interfaces para captura de dados sobre desenhos", Anais do SIBGRAPI V (1992) 169–175 [in Portuguese].
N. Franks, "Adding an extension language to your software", Dr. Dobb‘s Journal 16 (9) (Sep 1991) 34–43.
A. Goldberg, D. Robson, Smalltalk-80: the language and its implementation, Addison-Wesley, 1983.
R. Ierusalimschy, L. H. de Figueiredo, W. Celes Filho, "Reference manual of the programming language Lua", Monografias em Ciência da Computação 4/94, Departamento de Informática, PUC-Rio, 1994.
J. R. Levine, T. Mason, D. Brown, Lex & Yacc, O‘Reilly and Associates, 1992.
C. Nahaboo, A catalog of embedded languages, available from [email protected]
M. Richards, C. Whitby-Strevens, BCPL: the language and its compiler, Cambridge University Press, 1980.
B. Ryan, "Scripts unbounded", Byte 15 (8) (Aug 1990) 235–240.
R. Valdés, "Little languages, big questions", Dr. Dobb‘s Journal 16 (9) (Sep 1991) 16–25.

时间: 2024-11-03 21:57:14

Lua1.1 Lua 的设计和实现 (二)的相关文章

Lua1.1 Lua 的设计和实现 (一)

转载出处:http://my.oschina.net/xhan/blog/309613 说明: 这个文档是 Lua1.1 的 doc 目录里的 lua.ps 文件. 同时这个文档可以这里找到:http://www.lua.org/semish94.html 原文版权归原作者所有,这篇翻译只是作为学习之用.如果翻译有不当之处,请参考原文. --------------------以下是正文------------------ 应用程序扩展语言的设计和实现 摘要.我们描述 Lua 的设计和实现,一个

用MongoDB数据库来管理办公系统中文档型的表单和信息——通用流程化应用审批单设计思路(二,续)

1.办公系统中文档的定义 办公系统中的文档就是指对数据不敏感的业务,例如流程中的审批单.信息专栏.数据上报.信息记录等.而对于这些信息的管理,特别是时效性较强的管理记录,仍采用关系型数据库进行管理. (1)流程中审批单 流程中审批单由功能按钮区.特殊功能区.业务表单区.附件区.审批意见区等区域构成,其中,业务表单区理论上包含附件和意见,但是由于附件和意见的业务特殊性,需要单独进行管理,剩下的业务表单就可以看作文档了. 在一些流程审批业务中,业务信息有的是以Excel或word文件等方式专递,这样

《Programming in Lua 3》读书笔记(二十二)

日期:2014.8.6 PartⅣ The C API 26 Extending Your Application 使用Lua很重要的一点是用来做配置语言.配合主语言做一些功能的配置. 26.1 The Basics 有的时候程序需要配置一些功能信息,很多时候可能有许多别的方法比用lua做配置要更简单:如使用环境变量或者读取文件,读取文件涉及到文件的解析.如果使用Lua进行配置的话,相当于用lua文件替代了要读取的如csv.txt文件等. 使用Lua进行配置的时候,就需要使用Lua API去控制

JavaScript网站设计实践(二)实现导航栏当前所选页面的菜单项高亮显示

一.(一)中的代码还可以修改的地方. 在(一)中,如果是运行在服务器下,如apache等,可以把head和navigation的div抽取出来,放置在另一个html文件里,然后在页面中,include进来.这样,当要对导航栏进行修改时,只需要修改一个文件,而不用修改所有相关的页面文件.不过,我这里没有这样做,没有抽取出来. 二.实现当前页面的标识+不同页面的head头部背景图片的改变 现在在(一)实现的基础之上,来实现导航栏当前所选页面的菜单项高亮显示,让访问者一路了然知道"我正在这里"

数据结构课程设计题目十二_计算机学院学生会的打印机(优先队列)

本文出自:http://blog.csdn.net/svitter 题目12:计算机学院学生会的打印机(优先队列) 小明抱怨学生会的打印机不符合FIFO的原则,看到很多在他后面来打印的同学比他先打印出来.五分钟前,小明的文件就是下一个候选的,如今小明的文件又排到了后面.学生会的同学给小明解释说,学生会的打印机不是採用传统的队列方式,而是採用一种自定义的优先队列方式:每一个要打印的文件被赋予了一个从1到9的优先级(9最高,1最低).打印规定例如以下: 将队列中要打印的文件f从队列中拿出来: 假设在

用产品思维设计API(二)——数据解耦,才是前后分离的本质

用产品思维设计API(二)--数据解耦,才是前后分离的本质 前言 最近公司内部在重构项目代码,包括API方向的重构,期间遇到了很多的问题,不由得让我重新思考了下. - 一个优雅的API该如何设计? - 前后端分离之后,API真的解耦分离了吗? - 不断的版本迭代,API的兼容性该如何做? ps.这里所说的API仅为Web API,提供APP\WEB开发使用. 年前,我司内部的接口已经进入了一个完全的重构阶段,参考了市面上各大平台的API和文档,自己也总结出了很多的心得.这里向大家分享一下,接下来

用lua nginx module搭建一个二维码

用lua nginx module搭建一个二维码(qr code)生成器 作者 vinoca 發布於 2014年10月31日 如果有VPS,或者开源的路由器,安装一个nginx,添加lua-nginx-module,再编译安装qrencode for lua ,用下面的lua代码,访问http://youip/qr?t=hello就可以看到效果啦: local qr = require "qrencode" local args = ngx.req.get_uri_args() ngx

数据库设计原则(二)

1. 原始单据与实体之间的关系  可以是一对一.一对多.多对多的关系.在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体. 在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对应多个实体,或多张原始单证对应一个实体. 这里的实体可以理解为基本表.明确这种对应关系后,对我们设计录入界面大有好处. [例1]:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表.社会关系表.工作简历表.    这就是“一张原始单证对应多个实体”的典型例子. 2. 主键与

各开源框架使用与设计总结(二)

原文详见:http://www.ucai.cn/blogdetail/7032?mid=1&f=5 可以在线运行查看效果哦! 5.4.zephir高效开发模块 好的,讲到这里,衍生出一个小话题,就是开发模块. 在PHP里,开发模块,是一个很痛苦的过程.因为C语言,大家都知道,是出了名的难学的,值得高兴的是,也是Phalcon这个团队的童鞋们,也为我们准备了一个高效的开发模块的语言,称之为zephir.正因为扩展如此难以开发,但是扩展又是如此高效,所以我们要用高效的方式来开发扩展. git clo