在使用像Parsec这样的parsing器组合器库时,是否应该使用词法分析器?
在parsing器组合库(如Haskell的Parsec)中编写parsing器时,通常有两个select:
- 编写一个词法分析器,将您的
String
input分成标记,然后在[Token]
上执行parsing - 直接在
String
上写分析器组合器
第一种方法通常似乎是有道理的,因为许多parsinginput可以被理解为由空格分隔的令牌。
在其他地方,我曾经看到人们build议不要使用标记(或者扫描或者打点 ,有些人称之为),简单地被引用为主要原因。
勒索与不做之间的一般权衡是什么?
最重要的区别是,lexing会翻译你的input域 。
这是一个很好的结果
-
你不必再考虑空白了。 在直接(非lexing)分析器中,必须在允许空格的地方撒上
space
parsing器,这很容易被遗忘,而且如果空格必须分离所有的记号,则会使代码混乱。 -
你可以一个一个地考虑你的input,这对人类来说很容易。
但是,如果你执行lexing,你会得到这样的问题
-
你不能在
String
上使用普通的parsing器 – 例如,用一个库parsing一个数字函数parseFloat :: Parsec String s Float
(在一个stringinputstream上操作),你必须做一些事情,如takeNextToken :: TokenParser String
并execute
parseFloat
parsing器,检查parsing结果(通常是Either ErrorMessage a
)。 编写和限制可组合性是很麻烦的。 -
你已经调整了所有的错误信息。 如果您的parsing器的令牌在第20个令牌失败,在inputstring中的位置? 您必须手动将错误位置映射回inputstring,这是乏味的(在Parsec中,这意味着要调整所有的
SourcePos
值)。 -
错误报告通常更糟。 运行
string "hello" *> space *> float
在错误的input如"hello4"
会精确地告诉你在hello
之后会有空白,而词法分析器会声称已经find了"invalid token"
。 -
实际上,许多人认为是primefaces单位 ,并被词法分离的东西实际上对于词法分析器来说是非常“太难”的。 举例来说,string文字 – 突然之间,
"hello world"
不再是两个"hello
andworld"
标记(当然,如果引号没有被转义,就像\"
) – 这对parsing器来说是非常自然的,复杂的规则和一个词法分析器的特殊情况。 -
您不能重新使用标记上的parsing器。 如果你定义了如何parsing一个
String
的双String
,那么导出它,世界其他地方可以使用它; 他们无法首先运行您的(专用)标记器。 -
你被困住了。 在开发语言parsing时,使用词法分析器可能会导致您做出早期决策,修复之后可能需要更改的内容。 例如,假设您定义了一个包含一些
Float
标记的语言。 在某些时候,你想引入负文字(-3.4
和-3.4
) – 这可能是不可能的,因为词法分析器将空白解释为令牌分隔符。 使用仅parsing器的方法,您可以保持更多的灵活性,更轻松地更改您的语言。 这并不奇怪,因为parsing器是一个更复杂的工具,它固有地编码规则 。
总而言之,我会build议在大多数情况下编写无词法分析器。
最后,词法分析器只是一个“哑巴”分析器 – 如果你需要一个分析器,把它们合并成一个。
*从计算理论来看,我们知道所有正规语言都是上下文无关的语言 , 词法分析器通常是规则的,分析器上下文无关或甚至是上下文敏感的(像Parsec这样的一元分析器可以表示上下文敏感性)。