首页 > 解决方案 > 我的 parsec 解析器可以写弃用消息吗?

问题描述

我有一个 DSL,以及一个用 Haskell 和 Parsec 包编写的解析器。现在我想弃用 DSL 的特定语言功能。在下一个版本中,我希望解析器同时接受新语法和旧语法,但我希望解析器产生弃用消息。我找不到如何做到这一点。这可能吗?如果可以,怎么做?

标签: haskellparsec

解决方案


与其在解析期间发出消息,不如在解析结束时返回额外信息:是否遇到过时的语法。

ParsecT类型接受用户在解析期间设置的状态的类型参数:

ParsecT suma 是一个具有流类型 s、用户状态类型 u、底层 monad m 和返回类型 a 的解析器。Parsec 在用户状态下是严格的。

可以使用putState和设置用户状态modifyState。可以使用getState.

大多数 parsec 组合器在用户状态上是多态的。您自己的 DSL 的大多数组合器也应该是。但是语法中不推荐使用的部分的解析器应该在你的用户状态中设置一个“标志”。

像这样的东西:

import Text.Parsec
import Text.Parsec.Char
import Data.Functor.Identity

type Parser = ParsecT [Char] Bool Identity -- using a Bool state

myParser :: Parser Char
myParser = 
    try (do char 'a' 
            putState True 
            char 'b')
    <|> 
    try (do char 'a' 
            char 'c')

main :: IO ()
main = do
    print $ runParser ((,) <$> myParser <*> getState)  False "" "ab"
    print $ runParser ((,) <$> myParser <*> getState)  False "" "ac"
-- results:
-- Right ('b',True)
-- Right ('c',False)

当然,与其使用简单的布尔标志,不如将更多信息放入状态中。

请注意,如果子解析器回溯,则由子解析器设置的状态将被“遗忘”。对于我们的目的,这是正确的行为:否则,我们会得到由最终丢弃的分支触发的“误报”。


一个常见的 parsec 替代方案megaparsec。后者不允许在解析器类型本身中使用用户定义的状态,但可以使用StateT该类型的转换器来模拟它ParsecT


推荐阅读