使用Parsec

时间:2017-06-11 10:27:40

标签: parsing haskell

在遵循教程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;主角#?

1 个答案:

答案 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

请注意,由于您解析'#'两次,因此公共前缀变得更长且更复杂,因此效率稍低。