我如何概括从令牌中提取值的解析器?
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 Maybe
s:
{-# 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
选择器,尽管这可能不值得。
我正在 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 Maybe
s:
{-# 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
选择器,尽管这可能不值得。