我如何概括从令牌中提取值的解析器?

How do I generalize a Parser which extracts values from tokens?

我正在 Haskell 中为一种简单的语言创建一个解析器。解析器获取由单独的标记生成器生成的标记列表,并将其转换为结构化树。在编写此解析器时,我遇到了一个我想概括的重复出现的模式。以下是相关定义:

--Parser Definition
data Parser a b = Parser { runParser :: [a] -> Maybe ([a], b) }

--Token Definition
data Token = TokOp    { getTokOp :: Operator }
           | TokInt   { getTokInt :: Int }
           | TokIdent { getTokIdent :: String }
           | TokAssign
           | TokLParen
           | TokRParen
   deriving (Eq, Show)

解析器还为 MonadPlus 及其所有超级 类 定义了实例。以下是我试图概括的重复出现模式的两个示例:

-- Identifier Parser
identP :: Parser Token String
identP = Parser $ \input -> case input of
  TokIdent s : rest -> Just (rest, s)
  _                 -> Nothing

--Integer Parser
intP :: Parser Token Int
intP = Parser $ \input -> case input of
  TokInt n : rest -> Just (rest, n)
  _               -> Nothing

如您所见,这两个示例非常相似,但我看不出有什么办法可以一概而论。理想情况下,我想要一些 extractToken :: ?? -> Parser Token a 类型的函数,其中 a 是令牌包含的值。我猜解决方案涉及 >>=,但我对 Monads 的经验不足,无法弄清楚。这也可能是不可能的,我不确定。

似乎很难避免至少 一些 样板文件。一种简单的方法是手动将字段选择器定义为 return Maybes:

{-# LANGUAGE LambdaCase #-}

data Token = TokOp Operator
           | TokInt Int
           | TokIdent String
           | TokAssign
           | TokLParen
           | TokRParen
   deriving (Eq, Show)

getTokOp    = \case { TokOp x    -> Just x ; _ -> Nothing }
getTokInt   = \case { TokInt x   -> Just x ; _ -> Nothing }
getTokIdent = \case { TokIdent x -> Just x ; _ -> Nothing }

然后剩下的就是:

fieldP :: (Token -> Maybe a) -> Parser Token a
fieldP sel = Parser $ \case tok:rest -> (,) rest <$> sel tok
                            []       -> Nothing

opP    = fieldP getTokOp
identP = fieldP getTokIdent
intP   = fieldP getTokInt

您可以使用模板 Haskell 或泛型派生 getXXX 选择器,尽管这可能不值得。