《TypeScript图形渲染实战:2D架构设计与实现》 —2 使用TypeScript实现Doom 3词法解析器
第2章 使用TypeScript实现
Doom 3词法解析器
本章的目的是想让大家了解TypeScript中常用的一些语法及编程方式(如面向对象编程、面向接口编程、泛型编程,以及常用的设计模式等),因此,特别以面向接口的方式编写了一个Doom3(原id Software公司毁灭战士3游戏引擎)词法解析器,并且在此基础上实现了工厂模式和迭代器两种设计模式,使其支持接口的生成,以及使用迭代方式进行Token的解析输出。
Doom3引擎中所有的资源都存储在后缀名为.pk4的资源文件包中。该资源文件包实际上就是一个zip压缩文件,因此可以将.pk4后缀名更改为.zip,然后就可以直接使用,例如winzip解压程序进行解压并浏览该文件。
当打开pk4文件浏览后会发现一个事实:Doom3引擎中大部分的资源都是基于文本描述的(除了图片、视频及音频等资源外),并且基于一套简单的、统一的词法规则。如何将文本按照词法规则描述解析成有意义的标记(Token)是本章的另外一个重要目的。
最后对XMLHttpRequest对象进行二次封装用于向服务器请求资源。这样就能够利用第1章中部署的lite-server服务器,将所有存储在lite-server服务器上的文本文件、二进制文件、视频文件和音频文件传输到TypeScript(网页客户端)中进行处理。
实际上,实现的词法解析器不仅仅用于Doom3引擎相关资源的解析,通过些许扩展,还可以支持解析各种不同格式的ASCII编码文本文件,例如Wavefront的obj模型文件,以及mtl材质文件等。
2.1 Token与Tokenizer
为简单起见,使用JS代码作为示例来了解一下JS的Token相关内容,以加深对Token的理解。代码如下:
if ( b === true )
alert ( "true" ) ;
在浏览器中输入网址http://esprima.org/demo/parse.html后,将上述代码粘贴到左侧文本编辑框内,然后选择Tokens Tab选项,就会获得如下结果:
[
{
"type" : "Keyword" ,
"value" : "if"
} ,
{
"type" : "Punctuator" ,
"value" : "("
} ,
{
"type" : "Identifier" ,
"value" : "b"
} ,
{
"type" : "Punctuator" ,
"value" : ==="
} ,
{
"type" : "Boolean" ,
"value" : "true"
} ,
{
"type" : "Punctuator" ,
"value" : ")"
} ,
{
"type" : "Identifier" ,
"value" : "alert"
} ,
{
"type" : "Punctuator" ,
"value" : "("
} ,
{
"type" : "String" ,
"value": "\"true\""
} ,
{
"type" : "Punctuator" ,
"value" : ")"
} ,
{
"type" : "Punctuator" ,
"value" : ";"
}
]
通过上述代码可以看到,Esprima(ECMAScript词法语法解析器)会将JS源码解析成Token的集合表示。每个Token具有type属性,表示该Token的类型分类,并且具有value属性,表示该Token的值是什么。
由此可见,Token(标记或记号)就是指一组不可分割的字符或字符串,它能唯一地、没有歧义地标记出一种状态。从本质上来说,就是特殊的字符或字符串(例如if和=== 等)。而Esprima则是Tokenizer,其作用是将字符串表示的JS源码数据读取进来,按照预先设定的标准进行分类处理,处理的结果就是Token。
事实上,Esprima是一个ECMAScript解析器,包含词法解析和语法解析,最终会将JS源码解析成抽象语法树(Abstract Syntax Tree,AST),而在这里由于演示的原因,仅仅使用了Esprima的词法解析功能,并没有使用到语法解析功能。
2.1.1 Doom3文本文件格式
如果要实现一个特定文件格式的词法解析器,一定要了解该文件的词法特征,根据文件的词法特征抽象出分类规则,然后才能编码实现词法解析功能。因此本节来讲解一下Doom3引擎中的文本文件的相关规则,使用如下一段具有普遍性的文本字符串:
numMeshes 5
/*
* joints关键字定义了骨骼动画的bindPose
*/
joints {
"origin" -1 ( 0 0 0 ) ( -0.5 -0.5 -0.5 )
"Body" 0 ( -12.1038131714 0 79.004776001 ) ( -0.5 -0.5 -0.5 ) // origin
}
例如,numMeshes和joints等没有双引号的单词,作为关键字处理,也就是Doom3引擎预先定义好的具有特定含义的一些词,它们具有唯一性及不可更改性。
例如,"origin""Body"这些具有双引号的单词,作为标识符处理,这些标识符并非由Doom3引擎预先定义,而是由美术设计等相关人员或者模型制作动画师定义的名称。
在“/*”和“*/”之间的文字被Doom3引擎的词法解析器视为注释,和TypeScript一样,表示多行注释。斜杠“//”后的文字则被视为单行注释,这也和TypeScript的单行注释保持一致性。
大括号对“{ }”表示一个块状模块,可以将其视为一个区块分组,和TypeScript作用域类似。小括号对“( )”内部是使用浮点数表示的向量或矩阵数据,可以将其看成数组,需要注意的一点是,数组元素之间不是使用逗号分隔,而是使用空格符号进行分隔。
Doom3文本文件中的数据类型其实只有两种:字符串和数字,其中关键字和标识符都可以看成字符串类型,而数字可以分为整数和浮点数两种类型。
上述内容基本囊括了Doom3文本文件格式的关键之处,还有一些隐藏在深处的规则,将在源码实现的过程中进行描述。
- 点赞
- 收藏
- 关注作者
评论(0)