为语法编写词法规范的方法有很多种,但生成的令牌管理器的性能可能会因您的操作方式而有很大差异。
本节介绍了一些编写良好的词法规范的技巧。
内容
字符串文字
尽可能使用字符串字面量
避免使用相同标记的字符串文字
按长度排序字符串文字
词汇状态
尽量减少词汇状态的使用
尽可能使用 SKIP
避免将 SKIP 与词法操作和状态更改一起使用
尽可能避免使用 MORE
其他
单独使用 ~[]
避免选择性地使用 IGNORE_CASE
字符串文字
尽可能使用字符串字面量
尝试指定尽可能多的字符串文字。
这些由确定性有限自动机 (DFA) 识别,它比识别其他类型的复杂正则表达式所需的非确定性有限自动机 (NFA) 快得多。
例如,要跳过空白/制表符/新行:
SKIP : { " " | “\t” | “\n” }
比这样做更有效率:
SKIP : { < ([" ", “\t”, “\n”])+ > }
因为在第一种情况下你只有String文字,所以它会生成一个 DFA,而对于第二种情况它会生成一个 NFA。
避免使用相同标记的字符串文字
尽量避免为同一标记选择多个字符串文字。
例如:
< NONE : ““none”” | “‘none’” >
相反,为此有两种不同的令牌类型,并使用非终端,这是这些选择之间的选择。
上面的例子可以写成:
< NONE1 : ““none”” >
|
< NONE2 : “‘none’” >
并定义一个非终端称为None():
void None() : {}
{
|
}
这将使识别速度更快。请注意,如果选择是在两个复杂的正则表达式之间进行,那么可以选择。
按长度排序字符串文字
按长度增加的顺序指定所有字符串文字,即所有较短的字符串文字在较长的字符串文字之前。
这将有助于优化字符串文字所需的位向量。
词汇状态
尽量减少词汇状态的使用
尽量减少词汇状态的使用。
使用它们时,尝试将所有复杂的正则表达式移动到一个词法状态,让其他人只识别简单的字符串文字。
尽可能使用 SKIP
SKIP如果您不关心某些模式,请尽可能多地尝试。
在这里,你必须小心一点EOF。看到EOFafterSKIP很好,而看到EOFafter aMORE是词法错误。
避免将 SKIP 与词法操作和状态更改一起使用
尽量避免词法动作和词法状态随SKIP规范而变化,尤其是对于单个字符SKIP,如 , \t , \n
等)。
对于这种情况,会生成一个简单的循环来吃掉SKIP’ed 单个字符。因此,如果有与此相关的词汇动作或状态更改,则不可能以这种方式进行。
尽可能避免使用 MORE
尽量避免用MORE规范指定词法动作。
一般来说,每个人都MORE应该以 a TOKEN(or SPECIAL_TOKEN) finally 结尾,这样你就可以在该TOKEN级别执行操作,如果可能的话。
其他
~[]自己使用
尽量单独使用模式~[]。
例如,做
MORE : { < ~[] > }
比做更好
TOKEN : { < (~[])+ > }
当然,如果你的语法规定其中之一不能使用,那么你别无选择,只能尽量使用< ~[] >。
避免选择性地使用 IGNORE_CASE
IGNORE_CASE为某些正则表达式设置而不为处于相同词法状态的其他正则表达式设置会有严重的性能损失。
最佳做法是IGNORE_CASE在语法级别设置选项。如果那不可能,则尝试为词法状态中的所有正则表达式设置它。