reservedOp 的目的是创建一个解析器(您已将其命名为 m_reservedOp),它解析给定的运算符符号字符串,同时确保它不是更长的前缀运算符符号字符串。从源码中reservedOp的定义可以看出这一点:
reservedOp name =
lexeme $ try $
do{ _ <- string name
; notFollowedBy (opLetter languageDef) <?> ("end of " ++ show name)
}
请注意,提供的name 仅在它后面没有任何opLetter 符号时才会被解析。
在您的情况下,m_reservedOp "-" 无法解析字符串 "--2",因为即使它以有效运算符 "-" 开头,该字符串也作为更长有效运算符 "--" 的前缀出现。
在具有单字符运算符的语言中,您可能根本不想使用reservedOp,除非您想在不插入空格的情况下禁止相邻的运算符。只需使用symbol "-",它将始终解析"-",无论后面是什么(并消耗后面的空格,如果有的话)。此外,在具有固定集合 运算符(即没有用户定义的运算符)的语言中,您可能不会使用operator 解析器,因此您不需要opStart,或reservedOpNames。如果没有reservedOp 或operator,则不会使用opLetter 解析器,因此您也可以删除它。
这可能很令人困惑,Parsec 文档在解释“保留”机制应该如何工作方面做得很糟糕。这是一个入门:
让我们从标识符开始,而不是运算符。在允许用户定义标识符的典型语言(即几乎任何语言,因为“变量”和“函数”具有用户定义的名称)并且可能还有一些不允许作为标识符的保留字,相关设置在GenLanguageDef 中是:
identStart -- parser for first character of valid identifier
identLetter -- second and following characters of valid identifier
reservedNames -- list of reserved names not allowed as identifiers
使用GenTokenParser 对象创建的词位(吸收空白)解析器是:
-
identifier - 解析未知的用户定义标识符。它从identStart 解析一个字符,后跟零个或多个identLetters,直到第一个非identLetter。 (它从不解析部分标识符,因此它永远不会在桌面上留下更多 identLetters。)此外,它会检查该标识符是否不在列表中 reservedNames。
-
symbol - 解析给定的字符串。如果字符串是保留字,则不检查它是否不是更大的有效标识符的一部分。因此,symbol "for" 将匹配 foreground = "black" 的开头,这很少是您想要的。请注意,symbol 没有使用 identStart、identLetter 或 reservedNames。
-
reserved - 解析给定的字符串,然后确保它后面没有identLetter。因此,m_reserved "for" 将解析 for (i=1; ...,但 不会 解析 foreground = "black"。通常,提供的字符串将是一个有效的标识符,但不会对此进行检查,因此您可以根据需要编写 m_reserved "15" —— 使用具有通常类型的字母数字标识符的语言,这将解析 "15" 提供它后面没有字母或其他数字。此外,可能有点令人惊讶的是,没有检查提供的字符串是否在 reservedNames 中。
如果这对您有意义,那么操作员设置遵循完全相同的模式。相关设置为:
opStart -- parser for first character of valid operator
opLetter -- valid second and following operator chars, for multichar operators
reservedOpNames -- list of reserved operator names not allowed as user-defined operators
相关的解析器是:
-
operator - 解析未知的用户定义运算符,以 opStart 开头,后跟零个或多个 opLetters 直到第一个非 opLetter。因此,operator 应用于字符串 "--2" 将始终采用整个运算符 "--",而不仅仅是前缀 "-"。额外检查生成的运算符不在reservedOpNames 列表中。
-
symbol - 与标识符完全相同。它解析一个没有检查或引用opStart、opLetter或reservedOpNames的字符串,所以symbol "-"将解析字符串"--"的第一个字符就好了,留下第二个"-"字符稍后解析器。
-
reservedOp - 解析给定的字符串,确保它后面没有 opLetter。因此,m_reservedOp "-" 将解析 "-x" 的开头但不解析 "--2",假设 - 匹配 opLetter。和以前一样,不检查字符串是否在 reservedOpNames 中。