从零开始开发JVM语言(二)词法分析

目录戳这里java

词法分析的工做是,将输入的字符串转化为结构清晰的Tokengit

一般来讲,这个Token须要包含两样东西,词 和 类型github

##词编程

什么是词呢?例如,输入串以下:编程语言

val num = 3 * 7

从直觉上(语法高亮也帮助了咱们)能够看出,它由以下几部分组成性能

val num = 3 * 7.net

咱们不会把val拆成v al或者其余什么形式,由于它是一个“词”设计

“词”表示用于解析的最小单元。词的规定很宽泛,像emoji语言,它的一个词极可能就是一个emoji图像(虽然我没研究过不过大概如此吧。。)而对于主流的编程语言,词一般为一串字母,数字,某些特殊符号(运算符)code

注:以下方式的原理和自动机一致,可是性能没有自动机高。好处在于手写实现几乎不可能出错。blog

Latte中,词有以下规定 Scanner.java#L95

以下的符号

// SPLIT
".", ":", "::", "=", "+=", "-=", "*=", "/=", "%=", "<<", ">>", ">>>", "&", "^", "|", "~", "^^", "!", "&&", "||", "!=", "==", "!==", "===", "<", ">", "<=", ">=", "+", "-", "*", "/", "%", "++", "--", "@", "=:=", "!:=", "..", ".:", "..."
// LAYER
"->"
// STRING
"\"", "'", "`"
// NO_RECORD
" "
// ENDING
","
// COMMENT
";"
// PAIR
"{", "}", "[", "]", "(", ")"

被视为“只要遇到便须要分割的”符号,我把它们称作“分隔符”。什么意思呢?

观察

val num = 3 * 7

它写成这些形式都应当是同一种含义

val num=3*7
val num            =            3*        7
val num = 3    *7

虽而后两个看上去不太好看,但其含义不该与第一种相异

它为什么会被分割为那几个小块?很明显,空格是一个“分隔符”,在空格以前的视为一个Token,在空格以后的视为一个Token。而空格自己,比较特殊,它不可是“分隔符”,仍是“不须要被记录的分隔符”,将会被忽略。先后token将直接串联起来

* 也是一个“分隔符”,遇到*时将自动分隔先后,而且*自己也是一个词,将被记录在Token串中。

匹配串很简单,直接使用javaindexOf(str)便可。可是咱们想要的并非简单的匹配,而是“最优匹配”

例如

3<<2

首先匹配的应当为<<而不是<。由于<<长度为2,<长度为1。它们下标在同一位置,长度更长的将更加优先

3<(2<<1)

将优先匹配<,由于它的位置相比<<更靠前。

因此得出一个比较通用的结论: 最优匹配项为 1.下标最靠前 2.下标相同时长度最长

在java中也很是容易实现。首先将全部符号放到一个集合中,而后进行长度从大到小的排序。在匹配时按排序后的次序进行匹配,并记录下标。当下标更靠前时更换为匹配项为新的项

能够在这里看出来:排序 Scanner.java#L183 最优匹配 Scanner.java#L631

这样作法的扩展性极强,在修改时,只须要改动一下要修改的词便可。例如一开始设计的lambda为=>,后来通过调查(其实就是在一个java的群里问了问你们更喜欢那种= =)修改成->,仅仅改动一个字符串就完成了全部的修改。使用词法分析器生成器也能够方便的修改,可是手写自动机再修改就不太容易了。

不过这个方法也是一种DFA,和明确的状态机编程方式并无本质区别

不过要注意的是,按上述方式分隔将产生一个对于小数的莫名其妙的结果

1.2

匹配.,分隔为1,.,而后,遇到行末,便记录2

因此token串为1 . 2 而不是1.2。幸运的是咱们只会在小数上遇到这个问题(更准确的描述为 . 在不向前读时将产生歧义,这个歧义仅会在小数上出现)。在生成后作一个final check,转换一下就好了。或者每当遇到.都向前读一个字符,若为数字则,在生成时就生成为1.2

##类型 Token也有其自身的类型。

val num = 3 * 7

每一个token的类型为

val : 修饰符 MODIFIER
num : 合法的名称 VALID_NAME
=   : 符号 SYMBOL
3   : 数字 NUMBER_LITERAL
*   : 符号 SYMBOL
7   : 数字 NUMBER_LITERAL

实际上,记录了token的“内容”也就至关于记录了token的类型。好比val,再怎么变它也是一个修饰符。因此,标记类型仅仅是出于性能和扩展性考虑(固然,因为词法分析器须要检查token是否合法,因此在检查的时候顺带加上类型也是很天然的事)。

但愿看官能关注一下个人编程语言哦~ Latte

相关文章
相关标签/搜索