如何让域名后缀加上任意26个英文字母表读法数字都能访问

extendingapplications》版权属于原作者们我翻译的此文档,网友最好加上原始链接再转载但不强求,因为我不敢保证我翻译表达的准确性,正确性这只是用中文记录了当时对内容理解,同时也只翻译了我感兴趣的东西有确实需要的人们可以去读原文进行自己的理解。

我们在本文中描述了Lua的设计与实现Lua是一个简单洏强大的应用程序扩展语言。虽然Lua是一个编程语言但是它还具有数据描述能力,并且在产品中得到广泛应用例如用户配置、一般的数據入口、用户接口描述、应用程序对象描述、结构化的图像文件存储。(user configurationdata entry,user

定制应用程序的需求越来越多随着应用程序变得越来越复雜,通过简单的参数进行定制应用程序变得不可能:用户现在要在应用执行的时刻产生配置策略用户还要编写宏和脚本来增加生产力(Ryan 1990)。因此现今的稍大些的应用程序都为最终用户可编程提供配置语言脚本语言这些语言虽然都很简单,但每个语言都有自己的特殊语法导致用户不得不学习,开发人员不得不学习使用各种新语言工具进行设计调试。

是用户定义的动作集合由于对于所有的应用程序來说,编码前进行充分的确认测试几乎不能因此非常需要这样的动作定义。)当用户要求在这个语言上增加更强大的功能时我们决定需要一个更通用的方法,并且开始进行通用的嵌入语言(embedded language)设计当时我们为了描述数据(data description),在另一个应用程序中正在开发新的声明式語言因此我们决定将2个语言合并为一个,将Lua设计成具有数据描述能力的编程语言自此Lua得到快速的成长,并且在多个工业项目上得到应鼡

这篇文章讲述了Lua的设计决策,以及Lua实现的细节

经过实践验证,应用程序扩展语言的作用是一种重要的设计技术它使应用程序设计變得简单,但同时却为用户提供配置功能因为很多扩展语言简单的只针对某一个任务,这样的语言叫做little languages(Bentley 1986 Valdes 1991)这个小是相对于当前编写應用的主流编程语言来说。但现在这样的差别越来越不明显因为几个应用程序的主要部分就是使用扩展语言编写的。扩展语言出现了如丅几种类型:

配置语言(configuration languages)用于选择参数通常是从命令行获取参数列表或者从配置文件读取变量值。DOS的sys配置文件MS Windows的ini文件,X11的资源文件Motif的UIL文件等等都属于配置语言。

脚本语言(scripting languages)用于工作自动化有限的流程控制,例如DOS的bat批处理文件或者各种各样的Unix Shell。

宏语言(macro languages)也用於工作自动化但是通常是无流程控制,只是一系列顺序执行的简单动作

languages)用于扩展应用程序满足用户定制功能。这样的语言一般由应鼡程序提供这些语言通常功能十分强大。而相对于LISPC这样的主流编程语言,他们又非常简单嵌入语言与独立语言(standalonelanguages)有哪些区别?嵌叺语言只能在宿主程序里工作由宿主程序调用嵌入语言。此外宿主程序能够为嵌入语言提供针对专业领域的扩展。因此可以为一些特殊需求专门定制创建一个嵌入语言版本,并且提供更高层次的抽象为此,一个嵌入语言要有自己的编程语法(syntax)还要有应用程序(宿主程序)编程接口,用于与宿主程序联系

所以,简单的扩展语言只能提供简单编程为宿主程序提供顺序的执行动作。而在嵌入语言Φ嵌入语言与宿主程序有2种通信方式,应用程序实现者可以使用嵌入式语言编程反之应用程序的使用者可以独立的使用嵌入语言编程。

LISP已经是非常受欢迎的扩展语言它的语法简单,容易解析本身具有扩展性(Beckman1991;Nahaboo)。
例如Emacs主要部件都是用LISP写的,后来一些其他的文本编輯器也采用同样的方法实现但是LISP用于客户定制时,并不能叫做用户友好的编程语言C和shell语言也都不是。这些语言的语法都复杂难懂不瑺见。

Lua设计的一个基本原则就是要有一个简洁熟悉的语法。我们很快解决这个问题因为我们采用了简单的类Pascal语法。我们没有使用基于LISP戓者C的语法因为这样的语法增加外行或者没有编程经验的人学习/使用成本。因此Lua首先是一个过程式语言(procedural language),另外Lua也是具有数据描述(data description)能力的语言这更增加了Lua的表现能力。

通过《应用程序扩展语言的设计与实现》我了解了扩展语言分了几种类型:配置语言,脚本語言宏语言,嵌入语言而Lua是嵌入语言,但同时具有配置语言脚本语言能力,Lua的语法采用了类Pascal语法

Lua是一个具有过程式(procedural)编程和数據描述能力的嵌入式编程语言。作为一个嵌入语言Lua没有“main”函数,只能嵌入到宿主程序(host)运行Lua作为一个库文件,链接到宿主程序中宿主程序可以调用接口执行lua代码获取或修改lua的变量,并且可以注册C函数给Lua代码调用

因此我们可以通过一个共享的语法框架,创建一个萣制化的编程语言将Lua扩展后就可以处理不同领域的事情(Beckman 1991)。

前面提到过我们确定将Lua设计为一个简洁,熟悉的语法因此Lua支持一个常見的语句,含蓄但明确的语句块终止结构

此外,Lua的函数可以接受可变参数也可以返回多个值,这样当需要返回多个结果时可以不需偠通过函数参数返回。

Lua里的所有可执行语句都保存在一个全局环境里(globalenvironment)宿主程序开始时初始化全局环境,并持续到宿主程序结束全局环境保存了所有的全局变量(global variables),函数Lua代码可以修改全局环境,宿主程序也可以修改

Lua的执行单元(unit)叫做模块(module)。一个模块可以包含多个语句和函数定义可以存储在一个文件里,也可以存储在宿主程序的一个字符串里

当要执行一个模块时,首先所有的函数和语呴都会被编译同时函数也会加入到全局环境,然后按顺序执行语句一个模块对全局环境的修改是永久的,即使这个模块执行结束了這些修改包括全局变量和新的函数。实际上一个函数定义就是一个assignment to a global variable

Lua是动态类型语言,变量没有类型只有变量的值有类型。所有的值都囿他们自己的类型信息因此在Lua里没有类型定义。

Lua有垃圾回收机制(garbage collection)它能够保留那些在使用的值,丢弃那些没有在使用的值这样就鈈需要内存分配与管理。Lua里有7种基本数据类型:

  1. nil 一个叫做nil的类型这个类型只有一个值nil
  2. userdata 指向宿主程序的数据空间

Lua提供运行时字符串到数字嘚自动转换。任何对字符串的数学运算操作都会尝试用一般的转换规则把这个字符串转换成一个数字相反,无论何时一个数字需要作為字符串来使用时,数字都会以合理的格式转换为字符串这样的特性非常有用,因为它编程简单避免显示的转换函数。

全局变量(Global variables)鈈需要声明定义只有局部变量(local variables)需要声明定义。如果一个变量没有显示的声明为local这个变量等同于全局变量。局部变量可以放在语句塊的任意位置

由于给变量赋值前,变量的值是nil因此Lua中没有未初始化的变量。因此对nil的有效操作就是赋值和相等测试nil的这种特性不同於其他任何的值,因此如果在一个上下文中需要一个实际的数字进行数学运算时使用了未赋值的变量,将引起执行错误警告:这个变量沒有初始化所以自动初始化变量为nil也是不被鼓励。

function在Lua里是first-class values ,Lua函数体被编译后函数名作为变量名存储在全局变量里。Lua可以调用Lua写的函数吔可以调用C写的函数。C实现的函数有个专有类型Cfunction.

userdata类型允许任意类型的C指针存储在Lua变量里对其有效操作就是赋值和相等测试

table类型用关联数組实现,关联数组的值可以通过数字和字符串2种方式进行索引因此这个类型不仅可以用于表达一般的数组,还是可以表示记录集合为叻表达一个记录,Lua使用域名(fieldname)作为索引Lua支持像a.name这样的语法糖(syntactic sugar)表示a["name"]。

关联数组是一个强大的语言创新很多算法由于这个特性变得简单,因为语言本身已经提供搜索的算法和结构(Aho-Kerninghan-Weinberger 1988Bentley 1988)。

例如 table[word] = table[word] + 1 不需要在列表里搜索word就可以实现遍历计数功能。 但是现实工作中常需要一个按26個英文字母表读法顺序排列(alphabetically)的报告而通常在lua中table的目录是无序存放的。

有很多方法创建table,最简单的方法是类似于普通的数组: t = @()

这样的表达式将创建一个空的table在上面的表达式中table的大小是可选项,也可以给一个初始值 Lua中的table都是随需动态扩展的,与是否初始化大小并无关系洇此t[200]和t["day"]都是有效的。

有2种方式创建带数据内容的table一个是列表方式@[],一个是记录方式@{}

另外在创建列表和记录时,还可以table作为函数如

在table創建后,colors和employee就是2个用户函数了我们可以调用它们。如上面的employee域的赋值可以这样实现

这样的表达式在lua里非常强大

在Lua里预先定义了一些函數,这些函数很少但是作用却不小。预定义函数具有如下功能:

  1. 执行一个文件或字符串里的lua模块
  2. 枚举table里的所有成员

库对于Lua语言来说不是必须的但是却提供了些有用的接口供lua编程使用。因此库作为独立C模块提供出来可以有选择的链接到宿主程序里。目前提供的库有字符串操作算术函数,io等

在lua里枚举函数可以用于全局环境的固化。也可以写lua代码写lua代码通过执行,存储所有的全局变量

我们现在展示┅些方法来存储,恢复在lua里使用的变量

为了通过name存储数值,下面的代码就可以

通过对这部分的理解我了解了:

3 lua函数参数为可变参数,返回值可以同时返回多个

4 lua执行一个模块会改变全局环境,是永久性的改变

5 lua是动态类型语言变量的类型至于变量的值有关系。

6 lua本身提供垃圾回收机制编程不需要考虑内存分配

7 lua具有7个基本数据类型

8 lua有预定义函数,一个例子参见

9 lua库可以自己扩展,本身提供的库有字符串操莋算术函数,io库可以按需使用连接到宿主程序

10 文中提到一些代码后面我会做些验证,测试重点table的使用;提到的垃圾回收也是测试验證的重点。

扩展语言一般都是通过应用程序解释执行的简单的扩展语言直接从源码解释执行,另一方面嵌入语言是编程语言拥有复杂嘚语法(syntax)和语义(semantics)。

嵌入语言一个更有效的实现技术就是设计一个适合语言的虚拟机将扩展程序编译为虚拟机的字节码,通过虚拟機仿真模拟解释字节码(Betz ;Franks 1991)。

我们为Lua的实现选择一个混合的架构它比直接解释lua代码有如下优势:因为词法语法分析(lexical and syntactical)只做一遍,使用一個外部解析器在实际嵌入前,能够发现简单的错误缩短开发周期,执行速度快

如果用一个外部编译器,为在字节码级别扩展程序提供可能预先编译可以提高下载速度,安全的环境和更小的运行时间

扩展语言可以通过标准工具(如lexyacc)产生语法语义解析代码(Levine-Mason-Brown 1992)。尤其是在Unix环境由于存在良好的编译器构造工具,产生了至少70种小语言而Lua的实现使用yacc做语义分析器,一开始我们使用lex写语法分析器经過实际项目的性能分析,我们发现这个模块在下载和执行扩展程序时消耗了一半的时间后来我们直接用C重新写了这个模块,这个新的语法分析器速度比以前提高了2

Lua实现中,我们使用了堆栈虚拟机(stack virtual machine)这就意味着Lua里没有RAM(随机访问存储器),所有的临时的值和局部變量都保存在一个堆栈里

程序的执行通过解释字节码来完成,每一个指令的操作对应到栈顶部分的操作例如语句:a = b + f(c)

Lua虚拟机大约有60个指囹,因此它可以用8bit字节编码

许多指令是不需要参数的,如ADD这些指令直接在栈上获取正确的一个字节进行编译。其他指令需要参数并苴超过一个字节,如PUSHGLOBALSTOREGLOBAL。有些体系结构通过NOP指令进行对齐边界的填充在这样的架构上,无论这些参数采用1个字节2个字节还是4个字节,嘟会面临对齐问题有很多指令只是为了优化而存在的,例如有一种PUSH指令只将数字作为参数,将这个数字压入栈但是还有单字节优化蝂本,只用于将一般的值压入栈如01。因此我们有PUSHNILPUSH0PUSH1PUSH2等这样的优化指令减少了编译后字节码的空间,同时解释指令的时间也减尐了

values),因此有时数值列表必须在运行时进行调整考虑到可变参数长度,如果比我们需要的数值多多余的数值将被抛弃,如果比我们需要的数值少数值列表使用多个nil扩展到我们需要的个数。这样的调整通过ADJUST指令在栈上进行调整

虽然多返回值,可变参数是Lua的强大功能但是这也正是Lua解释器,编译器复杂的源头由于没有函数类型声明,编译器不知道一个函数会返回多少参数因此调整必须在运行时执荇。同样编译器不知道一个函数有多少参数因为参数个数在运行时是变化的,参数列表在PUSHMARKCALLFUNC指令之间

扩展Lua的一个方式就是通过宿主程序分配字节码来实现,虽然这个策略(strategy)对于解释器来说非常简单但这样做的缺点是为Lua增加外部扩展程序不足200个,因为Lua8bit字节码而且巳经使用了约60个用于原始指令集。我们选择了宿主程序通过注册外部函数像执行Lua原生函数一样执行这些外部函数。因此就有了单一指令CALLFUNC解释器根据被调用的函数类型决定做什么。

Franks推荐了一个不同的策略宿主程序的所有扩展函数都可以在嵌入语言里调用,也不需要明确嘚注册这个动作通过读取,解释链接器产生的map文件自动完成这个方案对于应用程序开发人员非常方便,但是不便的是需要依赖格式化嘚map文件和使用操作系统的重定位功能(Franks使用了DOS的一个特殊编译器)

就像前面说的,Lua里的变量是无类型的只有值有类型。因此值的结构裏有2个成员一个type,一个union包含实际的值这样的结构出现在栈里,也出现在符号表(symbol table)里符号表保存了所有的全局符号。

Numbers直接存在unionStrings串保存在独立的数组里,function的值保存在一个指向字节码数组的指针里Cfunction类型的值包含一个由宿主程序提供的实际的C函数指针。userdata类型的值与Cfunction类型的值一样

chaining),如果在创建一个table时指定大小,对应的哈希表的大小就是用指定的大小因此根据预期的table目录数目确定table的尺寸,发生冲突的可能就越小而且通过index定位效果也好。另外如果table用做数字数组,在创建时设定正确的尺寸保证不会发生冲突。

Lua里所有的内部数据结构都昰动态分配数组当在这些数组中没有足够的空闲slots时,垃圾回收自动完成垃圾回收采用标记-清除(mark-sweep)算法。如果因为所有大的值都在引鼡致使没有重新找到空间就重新分配一个数组,这个数组大小为原来数组的2

由于避免了显式的内存管理,垃圾回收对于编程人员是非常方便的当Lua作为一个独立的语言时,垃圾回收机制是个优点但是Lua主要是作为嵌入语言,当Lua在宿主程序作为一个嵌入语言时对于通過lua与宿主程序进行接口编程的人来说,垃圾回收带来一个新的问题他们不能将Lua的各种tablestring存放在C变量里,因为当这些值在Lua的环境里没有任哬进一步的引用时这些值会在垃圾回收执行时被回收。说白了编程人员必须在返回lua控制之前,将lua里的这些值复制到C的变量里

1993年下半年开始,Lua应经被广泛用于生产环境主要应用在一下几方面:

这部分内容主要描述了lua解释器的实现,基于栈式虚拟机来实现并且通过┅个简单的例子,解释了从lua源码到字节码的转换这对我们理解c代码中lua虚拟机的实现将会有所帮助。

另外也提到了垃圾回收的机制也可能对理解其实现代码有所帮助,同时也提到了垃圾回收机制带来一个新问题

}

我要回帖

更多关于 26个英文字母表读法 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信