Parsing 使用Maybe进行错误检测和报告

Parsing 使用Maybe进行错误检测和报告,parsing,haskell,text-parsing,Parsing,Haskell,Text Parsing,我正在用Haskell编写一个命题逻辑解析器。作为一个学习练习,我现在正在手工解析。最终我将解决Parsec。与此同时,我正试图把我的头绕在单子身上。特别是,我使用Maybe报告parse函数中的错误。我当前的问题是助手函数的一部分: parse' :: String -> (Maybe Wff, String) parse' ('[':rest) = (x, if null rest'' then ""

我正在用Haskell编写一个命题逻辑解析器。作为一个学习练习,我现在正在手工解析。最终我将解决Parsec。与此同时,我正试图把我的头绕在单子身上。特别是,我使用
Maybe
报告
parse
函数中的错误。我当前的问题是助手函数的一部分:

parse' :: String -> (Maybe Wff, String)
parse' ('[':rest) = (x, if null rest''
                        then ""
                        else tail rest'')
        where (a, rest') = parse' rest
              (b, rest'') = parse' (if null rest'
                                    then ""
                                    else tail rest')
              x = if null rest'
                     || null rest''
                     || head rest' /= '|'
                     || head rest'' /= ']'
                  then Nothing
                  else Or <$> a <*> b
char c' = StateT $ \str0 -> case str0 of
    []   -> Nothing
    c:cs -> runStateT (if (c == c')
        then StateT (\s -> Just ((), s))
        else StateT (\_ -> Nothing) ) cs
parse'::String->(可能是Wff,String)
解析“(”[”:rest)=(x,如果rest为空”
然后“
else尾架“”)
其中(a,rest')=解析“rest”
(b,rest“”)=parse'(如果rest为空)
然后“
其他尾座')
x=如果为空rest'
||空rest“”
||头枕“/=”|”
||头枕'/=']'
那就什么都没有了
还是b
(为了便于参考,可以找到完整的
parse
函数。)


此代码解析形式为
[a | B]
的命题,其中
a
B
是任意命题。如您所见,如果前面的递归调用导致
结果,我将使用最后一行中的应用程序样式来传播
结果。这允许我从
if
条件中取出
a==Nothing
b==Nothing
。如果,我如何使用
应用程序
Monad
实例来关闭该
的其余部分。这有点奇怪:

guard :: MonadPlus m => Bool -> m ()
MonadPlus
覆盖所有具有“空”大小写的monad。对于列表,这是
[]
;对于
可能
而言,它是
<代码>保护
采用布尔值;如果它是
False
,则计算为该空值;否则它的计算结果为
return()
。此行为在
do
表示法中最有用:

x = do guard (not $ null rest' || null rest'' || head rest' /= '|' || head rest'' /= ']')
       Or <$> a <*> b
x=do-guard(不是$null-rest'| | null-rest'| | head-rest'/='|'/='|'.| head-rest'/='])
还是b
这里发生的事情很简单。如果条件的计算结果为
True
,guard将返回
Just()
,然后将其忽略以支持
或b
(因为这就是
do
符号的工作方式)。但是,如果条件为
False
guard
返回
Nothing
,它通过
do
符号的其余部分传播,为您提供
Nothing
的最终结果:正是您想要的结果


为了使代码更具可读性,我还将把条件提取到
where
块中它自己的变量中。

我将实际向后做这个问题:我将从一元解开始,并从它向后工作到手动滚动解。这将产生与手工获得正确解决方案时相同的代码

一元解析器的典型类型签名的形式如下:

type Parser a = String -> Maybe (a, String)
注意表单的细微差别,在
Maybe
的外部有最后一个
字符串。两者都是有效的,但我更喜欢这个形式,因为我认为如果解析失败,剩下的代码<代码>字符串>代码>无效。
这种类型实际上是
StateT
的特例,其定义如下:

newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
请注意,如果我们选择:

s = String
m = Maybe
。。。我们返回
解析器
类型:

type Parser a = StateT String Maybe a

-- or: type Parser = StateT String Maybe
最酷的是,我们只需要手动定义一个解析器,即检索单个字符的解析器:

anyChar :: Parser Char
anyChar = StateT $ \str -> case str of
    []   -> Nothing
    c:cs -> Just (c, cs)
import Control.Monad

char :: Char -> Parser ()
char c' = do
    c <- anyChar
    guard (c == c')
请注意,如果我们删除了
StateT
包装,那么
anyChar
的类型将是:

anyChar :: String -> Maybe (Char, String)
当我们将其包装在
StateT
中时,它会变成:

anyChar :: StateT String Maybe Char
。。。这只是
解析器字符

一旦我们有了这个基本解析器,我们就可以使用
StateT
Monad
接口定义所有其他解析器。例如,让我们定义一个匹配单个字符的解析器:

anyChar :: Parser Char
anyChar = StateT $ \str -> case str of
    []   -> Nothing
    c:cs -> Just (c, cs)
import Control.Monad

char :: Char -> Parser ()
char c' = do
    c <- anyChar
    guard (c == c')
这两个实例的组合意味着
StateT s可能会
自动实现
MonadPlus
,因此我们可以使用
guard
,它会神奇地做“正确的事情”

有了这两个解析器,您的最终解析器变得非常容易编写:

data Wff = Or Char Char deriving (Show)

parseWff :: Parser Wff
parseWff = do
    char '['
    a <- anyChar
    char '|'
    b <- anyChar
    char ']'
    return (Or a b)
反向工作 这就引出了您最初的问题:如何手写相同的行为?我们将从
Monad
MonadPlus
实例向后推导出它们在幕后为我们做什么

为此,我们必须推断
StateT
Monad
MonadPlus
实例在其基本Monad为
时减少到什么程度。让我们从
StateT
Monad
实例开始:

instance (Monad m) => Monad (StateT s m) where
    return r = StateT (\s -> return (r, s))

    m >>= f  = StateT $ \s0 -> do
        (a, s1) <- runStateT m s0
        runStateT (f a) s1
如果我们将
Maybe
monad实例替换为
StateT
monad实例,我们得到:

instance Monad (StateT s Maybe) where
    return r = StateT (\s -> Just (r, s))

    m >>= f  = StateT $ \s0 -> case runStateT m s0 of
        Nothing      -> Nothing
        Just (a, s1) -> runStateT (f a) s1
我们可以做同样的事情来派生
StateT s的
Monad
实例。我们只需将
MonadPlus
实例作为
StateT
的实例,然后`也许:

instance (MonadPlus m) => MonadPlus (StateT s m) where
    mzero = StateT (\_ -> mzero)
    mplus (StateT f) (StateT g) = StateT (\s -> mplus (f s) (g s))

instance MonadPlus Maybe where
    mzero = Nothing
    mplus m1 m2 = case m1 of
        Just a  -> Just a
        Nothing -> case m2 of
            Just b  -> Just b
            Nothing -> Nothing
。。。并将它们合并为一个:

instance MonadPlus (StateT s Maybe) where
    mzero = StateT (\_ -> Nothing)
    mplus (StateT f) (StateT g) = StateT $ \s -> case f s of
        Just a  -> Just a
        Nothing -> case g s of
            Just b  -> Just b
            Nothing -> Nothing
现在我们可以推导出解析器在引擎盖下所做的工作。让我们从
char
解析器开始:

char c' = do
    c <- anyChar
    guard (c == c')
我们刚刚推导出了
(>>=)
状态的作用,可能是
,因此让我们用以下内容代替:

char c' = StateT $ \str0 -> case runStateT anyChar str0 of
        Nothing      -> Nothing
        Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
char c' = StateT $ \str0 -> case runStateT (StateT $ \str -> case str of
        []   -> Nothing
        c:cs -> Just (c, cs) ) str0 of
    Nothing -> Nothing
    Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
我们已经知道了
anyChar
的定义,因此让我们将其替换为:

char c' = StateT $ \str0 -> case runStateT anyChar str0 of
        Nothing      -> Nothing
        Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
char c' = StateT $ \str0 -> case runStateT (StateT $ \str -> case str of
        []   -> Nothing
        c:cs -> Just (c, cs) ) str0 of
    Nothing -> Nothing
    Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
我们还知道
runStateT
StateT
相反,因此:

char c' = StateT $ \str0 -> case (\str -> case str of
        []   -> Nothing
        c:cs -> Just (c, cs) ) str0 of
    Nothing -> Nothing
    Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
然后,我们可以将lambda应用于
str0

char c' = StateT $ \str0 -> case (case str0 of
        []   -> Nothing
        c:cs -> Just (c, cs) ) of
    Nothing -> Nothing
    Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
现在,我们将外部case语句分布在内部case语句上:

char c' = StateT $ \str0 -> case str0 of
    []   -> case Nothing of
        Nothing -> Nothing
        Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
    c:cs -> case Just (c, cs) of
        Nothing -> Nothing
        Just (a, str1) -> runStateT ((\c -> guard (c == c')) a) str1
。。。并评估案例陈述:

char c' = StateT $ \str0 -> case str0 of
    []   -> Nothing
    c:cs -> runStateT ((\c -> guard (c == c')) c) cs
然后我们可以将lambda应用于
c

char c' = StateT $ \str0 -> case str0 of
    []   -> Nothing
    c:cs -> runStateT (guard (c == c')) cs
为了进一步简化,我们需要
guard pred = if pred then StateT (\s -> Just ((), s)) else StateT (\_ -> Nothing)
char c' = StateT $ \str0 -> case str0 of
    []   -> Nothing
    c:cs -> runStateT (if (c == c')
        then StateT (\s -> Just ((), s))
        else StateT (\_ -> Nothing) ) cs
char c' = StateT $ \str0 -> case str0 of
    []   -> Nothing
    c:cs -> (if (c == c')
        then (\s -> Just ((), s))
        else (\_ -> Nothing) ) cs
char c' = StateT $ \str0 -> case str0 of
    []   -> Nothing
    c:cs -> if (c == c') then Just ((), cs)  else Nothing
parseWff = do
    char '['
    a <- anyChar
    char '|'
    b <- anyChar
    char ']'
    return (Or a b)

parseWff = StateT $ \str0 -> case str0 of
    []     -> Nothing
    c1:str1 -> if (c1 == '[')
        then case str1 of
            []      -> Nothing
            c2:str2 -> case str2 of
                []      -> Nothing
                c3:str3 -> if (c3 == '|')
                    then case str3 of
                        []      -> Nothing
                        c4:str4 -> case str4 of
                            []      -> Nothing
                            c5:str5 -> if (c5 == ']')
                                then Just (Or c2 c4, str5)
                                else Nothing
                    else Nothing
        else Nothing
parseWff = StateT $ \str0 -> case str0 of
    '[':c2:'|':c4:']':str5 -> Just (Or c2 c4, str5)
    _                      -> Nothing
parse :: String -> Maybe Wff
parse s = do
  (x, rest) <- parse' s
  guard $ null rest
  Just x
    where  parse' ('~':rest) = do
             guard . not $ null rest
             (a, rest') <- parse' rest
             Just (Not a, rest')
           parse' ('[':rest) = do
             guard . not $ null rest
             (a, rest') <- parse' rest
             guard . not $ null rest'
             guard $ head rest' == '|'
             (b, rest'') <- parse' $ tail rest'
             guard . not $ null rest''
             guard $ head rest'' == ']'
             Just (a `Or` b, tail rest'')
           parse' (c:rest) = do
             guard $ isLower c
             Just (Var c, rest)
           parse' [] = Nothing
parse :: String -> Maybe Wff
parse s = do
  (x, "") <- parse' s
  Just x
    where  parse' ('~':rest) = do
             (a, rest') <- parse' rest
             Just (Not a, rest')
           parse' ('[':rest) = do
             (a, ('|':rest')) <- parse' rest
             (b, (']':rest'')) <- parse' rest'
             Just (a `Or` b, rest'')
           parse' (c:rest) = do
             guard $ isLower c
             Just (Var c, rest)
           parse' [] = Nothing