自制操作系统Antz(14)——实现内置编程语言(词法解析器)
AntzScript
? The language executed in the Antz system.
基于以实现的内置vim,接下来需要实现Antz系统中内置语言AntzScript。
vim |
---|
![]() |
一、概述
1. 解释器与编译器
1.1 解释器
解释器根据程序中的算法执行运算。简单地说,它是一种用于执行程序的软件。
1.2 编译器
编译器能将某种语言写成的程序转换为另一种语言的程序。通常它会将源程序转换为机器语言程序。编译器转换程序的行为称为编译,转换前的程序称为源程序。
- Java虚拟机为了提高性能,会在执行过程中通过编译器将一部分Java二进制代码直接转换为机器语言使用。
过去的编译器编译过程非常费时,不过由于编译后实际执行的是机器语言,因此执行速度很快。而解释器,会在程序输入的同时立即执行,执行速度较慢。这就是两者的基本区别。
在为Antz制作内置脚本语言时,因为是直接执行,处理器属于解释器,不过,为了提高性能,该解释器内部将采用编译器。
基本流程如下
![]() |
![]() |
2. Token
语言处理器的第一个组成部分是词法分析器(lexer)。程序的源代码最初只是一长串字符串,这样的字符串很难处理,语言处理器通常会首先将字符串中的字符以解析单元分组,切割成多个Token,这就是词法分析。
解析结果 |
---|
![]() |
![]() |
Token类中需要对源代码分割成最小单元
,并解析出变量类型
,数值量
,符号
,标识符
等
Token |
---|
![]() |
Token中k代表单元含义,lexeme代表其在代码中的内容。kindMap是一个提前预置关系转换符表。在需要添加其他关键字时可以直接在KindMap中添加。 |
![]() |
词法解析器的实现方法有手工构造法和正则表达式的方法。
手工构造法-状态图 |
---|
![]() |
这种方法较为复杂,需要大量的代码,而正则表达式的方法较为便捷。
二、实现词法解析
借助正则表达式库能简单地实现词法分析器。下表列出的记号在大多数情况下都能使用。例如,.*\.java
指的是以.java
结束的任意长度的字符串模式。.*\.
由两部分组成,.*
表示由任意字符组成的任意长度的字符串模式,\.
表示与句点字符相匹配的字符串模式。(java|javax)\..*
则表示由java.
或javax.
起始的任意长度的字符串模式。
元字符 | 含义 |
---|---|
. | 与任意字符匹配 |
[0-9] | 与0至9中的某个数字匹配 |
[^0-9] | 与0至9这些数字之外的的某个字符匹配 |
pattern* | 模式pattern至少重复出现0次 |
pattern+ | 模式pattern至少重复出现1次 |
pattern? | 模式pattern出现0次或1次 |
pattern1 | pattern2 |
() | 将括号内视为一个完整的模式 |
\c | 与单个字符c(元字符*或.等)匹配 |
整型字面量的匹配
[0-9]+
标识符的匹配
[A-Z_a-z][A-Z_a-z0-9]*
- 以字母、下划线开头,之后仅包含有字母、数字、下划线的就是标识符的定义规则。
[A-Z_a-z][A-Z_a-z0-9]*|==|<=|>=|&&|\|\||\p{Punct}
最后的\p{Punct}
表示与任意一个符号字符匹配。模式\|\|
将会匹配||
。由于|是正则表达式的元字符,因此在使用时必须添加反斜线转义。
最后需要定义的是字符串字面量。由于不得不处理各种转义字符,字符串字面量的定义略微复杂。
"(\\"|\\\\|\\n|[^"])*"
首先,从整体上看,这是一个"(pattern)*"
形式的模式,即双引号内是一个与pattern重复出现至少0次的结果匹配的字符串。其中,模式pattern与\"
、\\
、\n
匹配,同时也与除"
之外任意一个字符匹配。
所以我们可以得到一个下图的匹配规则regexPat。
ArrayList<Token> tokens
是词法解析器Lexer
中已解析的Token
。
源码source作为参数传入Lexer
的add
方法中。
在创建Token时,单元作为构造函数的参数。同时变量,数字值等单元我们无法通过KindMap
类来判断k值,所以需要在这里判断。
判断是否是变量类型 |
---|
![]() |
判断是否是数值类型 |
---|
![]() |
判断是否是字符串类型 |
---|
![]() |
三、效果
测试代码 |
---|
![]() |
Token解析 |
---|
![]() |
![]() |
将源代码转换为了相应的Token单元,这就是词法解析的主要工作了。
接下来按照源代码获得转换后的直译代码。
效果图 |
---|
![]() |