gpt4 book ai didi

parsing - 在 Haskell 中从头开始编写解析器

转载 作者:行者123 更新时间:2023-12-03 10:54:46 27 4
gpt4 key购买 nike

有没有什么好的教程可以从头开始在 Haskell 中为给定的语法编写解析器?

我发现:

  • parsing expressions and statements (HaskellWiki)
  • Parsing a simple imperative language (HaskellWiki)
  • Using parsec (Real World Haskell)

  • 但是所有这些都使用 parsec 库,虽然这对于工业应用程序可能很有趣,但我特别在寻找不使用复杂库的“自下而上”的示例。

    我发现唯一使用“基本”Haskell 的是: Parsing with Haskell
    它使用了一些非常陌生的语法(很难区分程序的一部分或仅是“伪代码”),并且没有明确的语法定义。

    任何建议都非常感谢!

    最佳答案

    从头开始构建 Parsec 实际上非常容易。实际的库代码本身是高度概括和优化的,这会扭曲核心抽象,但如果您只是从头开始构建东西以了解更多关于正在发生的事情,您只需几行代码即可编写它。我会建一个稍微弱一点的Applicative下面的解析器。

    本质上,我们想要生成 Applicative , Parser连同原始解析器值

    satisfy :: (Char -> Bool) -> Parser Char

    和一些组合符,例如 try , 如果解析器失败,它会“撤消”解析器
    try :: Parser a -> Parser a

    orElse如果第一个解析器失败,这允许我们继续使用第二个解析器。通常这实际上是用中缀组合器 (<|>) 编写的。
    orElse, (<|>) :: Parser a -> Parser a -> Parser a

    由于我们的 Applicative需要跟踪当前流状态并能够失败,我们将通过组合状态 Applicative 来构建它和 Either适用的。
    type Error = String
    newtype Parser a = P { unP :: String -> (String, Either Error a) }

    instance Functor Parser where
    fmap f (P st) = P $ \stream -> case st stream of
    (res, Left err) -> (res, Left err)
    (res, Right a ) -> (res, Right (f a))

    instance Applicative Parser where
    pure a = P (\stream -> (stream, Right a))
    P ff <*> P xx = P $ \stream0 -> case ff stream0 of -- produce an f
    (stream1, Left err) -> (stream1, Left err)
    (stream1, Right f ) -> case xx stream1 of -- produce an x
    (stream2, Left err) -> (stream2, Left err)
    (stream2, Right x ) -> (stream2, Right (f x)) -- return (f x)

    如果我们关注 (<*>) Applicative 中的方法实例仔细我们看到它只是将流传递到 f -生产 Parser ,获取结果流,如果成功,将其传递给 x -生产 Parser如果他们都成功了,它会返回他们的申请 (f x) .这意味着如果我们有一个产生函数的解析器和一个产生参数的解析器,我们可以用 (<*>) 对它们进行排序。
    -- given
    parseChar :: Char -> Parser Char

    parseHi :: Parser (Char, Char) -- parses 'h' then 'i'
    parseHi = pure (,) <$> parseChar 'h' <*> parseChar 'i'

    我们可以使用 Applicative 的机制也可以构建所需的组合器。这里是 satisfy
    -- | Peek at the next character and return successfully if it satisfies a predicate
    satisfy :: (Char -> Bool) -> Parser Char
    satisfy f = P $ \stream -> case stream of
    [] -> ([], Left "end of stream")
    (c:cs) | f c -> (cs, Right c)
    | otherwise -> (cs, Left "did not satisfy")

    这里是 try
    -- | Run a parser but if it fails revert the stream to it's original state
    try :: Parser a -> Parser a
    try (P f) = P $ \stream0 -> case f stream0 of
    (_ , Left err) -> (stream0, Left err)
    (stream1, Right a ) -> (stream1, Right a )

    这里是 orElse
    orElse :: Parser a -> Parser a -> Parser a
    orElse (P f1) (P f2) = P $ \stream0 -> case f1 stream0 of
    (stream1, Left err) -> f2 stream1
    (stream1, Right a ) -> (stream1, Right a)

    通常在这一点上,我们还注意到 Parser形成 Alternative orElse 的实例如果我们还提供一个立即失败的解析器, empty
    instance Alternative Parser where
    empty = P $ \stream -> (stream, Left "empty")
    (<|>) = orElse

    many = manyParser
    some = someParser

    我们可以写 manyParsersomeParser作为重复运行解析器的组合器。
    -- | 0 or more
    manyParser :: Parser a -> Parser [a]
    manyParser (P f) = P go where
    go stream = case f stream of
    (_ , Left err) -> (stream, Right []) -- throws away the error
    (stream', Right a ) -> case go stream' of
    (streamFin, Left err) -> (streamFin, Left err)
    (streamFin, Right as) -> (streamFin, Right (a : as))

    -- | 1 or more
    someParser :: Parser a -> Parser [a]
    someParser (P f) = P $ \stream -> case f stream of
    (stream', Left err) -> (stream', Left err)
    (stream', Right a ) ->
    let (P fmany) = manyParser (P f)
    in case fmany stream' of
    (stream'', Left err) -> (stream'', Left err)
    (stream'', Right as) -> (stream'', Right (a:as))

    从这里我们可以开始在更高层次的抽象上工作。
    char :: Char -> Parser Char
    char c = satisfy (== c)

    string :: String -> Parser String
    string [] = pure []
    string (c:cs) = (:) <$> char c <*> string cs

    oneOf :: [Char] -> Parser Char
    oneOf cs = satisfy (`elem` cs)

    parens :: Parser a -> Parser a
    parens parseA = dropFirstAndLast <$> char '(' <*> parseA <*> char ')'
    where
    dropFirstAndLast _ a _ = a

    关于parsing - 在 Haskell 中从头开始编写解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20660782/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com