在遵循教程Write yourself a Scheme in 48 hours的过程中,我试图增强我的解析代码以创建对十六进制,八进制,二进制和十进制文字的支持。
import Text.ParserCombinators.Parsec hiding (spaces)
import Control.Monad
import Numeric
hexChar :: Parser Char
hexChar = ...
octChar :: Parser Char
octChar = ...
hexNumber :: Parser Integer
hexNumber = do
char '#'
char 'x'
s <- many1 hexChar
return $ (fst . head) readHex s
octNumber :: Parser Integer
octNumber = do
char '#'
char 'o'
s <- many1 hexChar
return $ (fst . head) readOct s
如果我们在本讨论中忘记了十进制和二进制数字:
parseNumber :: Parse Integer
parseNumber = hexNumber <|> octNumber
然后此解析器将无法识别八进制数。这似乎与从十六进制数字中分辨和八进制所需的前瞻字符数量有关(如果我们在语法中删除前导&#39;#&#39;那么
解析器将工作)。因此,我们似乎不得不重新审视代码并将因子分解为“分解”。领先的&#39;#&#39;可以这么说,将char '#'
放在各个解析器中并定义:
parseNumber = char '#' >> (hexNumber <|> octNumber)
这很好,但我发现代码不太令人愉快。不知何故,如果我有一个名为hexNumber
的函数,我希望它能识别#xffff
(这是正确的Scheme语法)而不是xffff
。这是我必须要忍受的事情,还是有办法解决这个问题?强制分解&#39;主角#?
答案 0 :(得分:1)
如果(<|>)
的第一个参数在消耗了一些输入后失败,那么它会在没有尝试第二个选项的情况下立即失败。如果第一个参数中的失败导致使用第二个参数重试,则可以使用try
来避免消耗输入。在hexNumber
中,只有当以下字符与'#'
匹配时,您才必须使用'x'
。
hexNumber :: Parser Integer
hexNumber = do
try $ char '#' >> char 'x'
s <- many1 hexChar
return $ (fst . head) readHex s
octNumber :: Parser Integer
octNumber = do
try $ char '#' >> char 'o'
s <- many1 hexChar
return $ (fst . head) readOct s
请注意,由于您解析'#'
两次,因此公共前缀变得更长且更复杂,因此效率稍低。